Nest SMS Documentations

Estimated time: ~15 minutes setup + up to 48 hours DNS propagation

API Documentation

Complete guide to integrate with our SMS API. Use your API token to authenticate all requests.

Quick Start

1. Generate an API Key

Go to API Integration → API Token in the sidebar and click Add Token. Copy the key immediately — it will only be shown once.

2. Add the Key to Your Request

Include your API key using the X-API-Key header (recommended):

text
X-API-Key: nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

3. Send Your First SMS

Use the POST /api/v1/sms/send endpoint:

bash
curl -X POST "https://your-domain.com/api/v1/sms/send" \
  -H "X-API-Key: nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "9840123456",
    "message": "Hello from NestSMS!",
    "sender_id": "NestSMS"
  }'

4. Save the job_id

The response includes a job_id. Use it with GET /api/v1/sms/status/:messageId to track delivery status.

Authentication

Method 1: X-API-Key Header Recommended

Send your API key in the X-API-Key header. This is the preferred method for all API requests.

javascript
const response = await fetch('https://your-domain.com/api/v1/sms/send', {
  method: 'POST',
  headers: {
    'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '9840123456',
    message: 'Hello from NestSMS!',
    sender_id: 'NestSMS'
  })
});

const result = await response.json();

Method 2: Authorization Bearer Token

Alternatively, pass your API key as a Bearer token in the Authorization header.

javascript
const response = await fetch('https://your-domain.com/api/v1/sms/send', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '9840123456',
    message: 'Hello from NestSMS!'
  })
});

Python Example

python
import os
import requests

API_KEY = os.environ.get('NESTSMS_API_KEY')  # never hardcode
BASE_URL = 'https://your-domain.com/api/v1/sms'

HEADERS = {
    'X-API-Key': API_KEY,
    'Content-Type': 'application/json'
}

response = requests.post(
    f'{BASE_URL}/send',
    headers=HEADERS,
    json={
        'to': '9840123456',
        'message': 'Hello from NestSMS!',
        'sender_id': 'NestSMS'
    }
)

result = response.json()

Key Prefixes

nsms_live_ — Live key for production. Charges real balance.

⚠️ Security Warning

Never hardcode API keys in source code. Use environment variables. Never expose keys in client-side JavaScript or mobile apps.

SMS Endpoints

POST/api/v1/sms/send

Send a single SMS or bulk SMS to multiple recipients.

Parameters
to(string | array)*required

Recipient number(s) — single number, array, or comma-separated string (e.g. '9840123456' or ['9840123456', '9851234567'])

message(string)*required

Message content (max 720 chars for text, 335 for unicode)

sender_id(string)

Sender ID to use. Defaults to your account's default sender ID

type(string)

Encoding: 'text', 'unicode', or 'flash'. Default: 'text'

message_type(string)

Purpose: 'promotional', 'transactional', or 'otp'. Default: 'transactional'

Example Request
javascript
// Single SMS
const sendSingleSMS = async () => {
  const response = await fetch('https://your-domain.com/api/v1/sms/send', {
    method: 'POST',
    headers: {
      'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      to: '9840123456',
      message: 'Hello from NestSMS!',
      sender_id: 'NestSMS',
      type: 'text',
      message_type: 'transactional'
    })
  });
  return await response.json();
};

// Bulk SMS (array)
const sendBulkSMS = async () => {
  const response = await fetch('https://your-domain.com/api/v1/sms/send', {
    method: 'POST',
    headers: {
      'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      to: ['9840123456', '9851234567', '9860123456'],
      message: 'Bulk message to all recipients!',
      sender_id: 'NestSMS',
      message_type: 'promotional'
    })
  });
  return await response.json();
};
Example Response
json
// Single SMS response
{
  "success": true,
  "message": "SMS queued successfully",
  "data": {
    "job_id": "job-uuid-here",
    "cost": 1.0,
    "remaining_balance": 499.0
  }
}

// Bulk SMS response
{
  "success": true,
  "message": "Bulk SMS queued successfully",
  "data": {
    "queued_count": 3,
    "total_cost": 3.0,
    "remaining_balance": 497.0,
    "job_ids": ["job-uuid-1", "job-uuid-2", "job-uuid-3"]
  }
}
GET/api/v1/sms/balance

Get your current wallet balance and usage statistics.

Example Request
javascript
const checkBalance = async () => {
  const response = await fetch('https://your-domain.com/api/v1/sms/balance', {
    method: 'GET',
    headers: {
      'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    }
  });
  return await response.json();
};
Example Response
json
{
  "success": true,
  "data": {
    "balance": 500.0,
    "currency": "NPR",
    "per_message_cost": 1.0,
    "messages_remaining": 500,
    "usage": {
      "total_messages_sent": 1234,
      "successful": 1200,
      "delivered": 1150,
      "failed": 34,
      "total_spent": 1234.0
    }
  }
}
GET/api/v1/sms/status/:messageId

Check the delivery status of a specific message by its ID.

Parameters
messageId(string (UUID))*required

Message ID returned from the send response

Example Request
javascript
const getMessageStatus = async (messageId) => {
  const response = await fetch(
    'https://your-domain.com/api/v1/sms/status/' + messageId,
    {
      method: 'GET',
      headers: {
        'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
      }
    }
  );
  return await response.json();
};
Example Response
json
{
  "success": true,
  "data": {
    "message_id": "msg-uuid-here",
    "status": "delivered",
    "recipient": "9840123456",
    "sender_id": "NestSMS",
    "message_type": "text",
    "sent_at": "2025-11-27T08:30:00.000Z",
    "delivered_at": "2025-11-27T08:30:15.000Z",
    "failed_at": null,
    "error_code": null,
    "error_message": null,
    "cost": 1.0,
    "delivery_reports": [
      {
        "status": "delivered",
        "received_at": "2025-11-27T08:30:15.000Z",
        "network": "NTC"
      }
    ]
  }
}

Template Usage

Create and use reusable message templates with dynamic variables. Templates let you define a message once and reuse it with placeholders.

Creating a Template

Go to Settings → Templates → New Template and define your message with placeholders:

text
Hello {{name}}, your OTP is {{otp}}. Valid for {{expiry}} minutes.

Using a Template in the API

Pass the template_id and a variables map instead of a raw message:

javascript
const response = await fetch('https://your-domain.com/api/v1/sms/send', {
  method: 'POST',
  headers: {
    'X-API-Key': 'nsms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '9840123456',
    template_id: 'tpl_otp_123',
    variables: {
      name: 'Aarav',
      otp: '847291',
      expiry: '5'
    }
  })
});
Tip: Templates that have been pre-approved by your carrier can bypass content filtering delays, making them ideal for OTP and transactional messages.

Error Codes Reference

All error responses follow this shape:{ "success": false, "error": "Human-readable message", "code": "ERROR_CODE" }

CodeHTTPDescriptionFix
NO_API_KEY401No API key in requestAdd X-API-Key header
INVALID_API_KEY401Key not found or malformedCheck your key value
API_KEY_EXPIRED401Key has passed its expiry dateGenerate a new key
ORG_INACTIVE403Organization is deactivatedContact support
IP_RESTRICTED403Request from a non-whitelisted IPAdd your IP to the key's whitelist
INSUFFICIENT_PERMISSIONS403Key missing required permissionAdd the permission when creating the key
MISSING_TO400'to' field not providedInclude recipient number(s)
MISSING_MESSAGE400'message' field not providedInclude message content
INVALID_TO_FORMAT400Invalid phone number formatUse numeric format e.g. 9840123456
TOO_MANY_RECIPIENTS400More than 1000 recipients in one requestSplit into batches of ≤1000
MESSAGE_TOO_LONG400Message exceeds max lengthMax 720 chars (text), 335 (unicode)
NO_SENDER_ID400No sender ID configuredSet up a sender ID in Settings
INVALID_SENDER_ID400Sender ID not found or not approvedUse an approved sender ID
INSUFFICIENT_BALANCE400Wallet balance too lowTop up your wallet
MESSAGE_NOT_FOUND404messageId not foundCheck the ID from the send response
ACCESS_DENIED403Resource belongs to another organizationVerify you're using the correct key
RATE_LIMIT_EXCEEDED429Too many requests in the time windowWait retry_after seconds before retrying

Rate Limits

Default limits apply per API key. Higher limits are available on request.

WindowDefault Limit
Per minute60 requests
Per hour1,000 requests

Rate limited response (429)

{
  "success": false,
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 30
}

Use the retry_after value (in seconds) before retrying.

Need Help?

If you have questions or encounter issues, refer to the documentation or contact our support team.

Documentation

Full API reference and integration guides

Support

Contact us for technical assistance

White-Label Setup Guide

This guide walks you through all required configuration steps to deploy your fully branded SMS service to your clients.

Getting Started

Welcome to the NestSMS White-Label platform. Complete each step in order — skipping steps may cause configuration errors that are difficult to diagnose.

  • Make sure you have reseller account on the platform.
  • Access to your domain's DNS panel (Cloudflare, GoDaddy, Namecheap, etc.)
  • A reseller account on the platform. Your brand's primary color hex code, logo file (SVG or PNG), and favicon (32×32 px)
Note: Make sure you have above prerequisites ready before you start the setup process. If you have any questions or need assistance, contact our support team.
1

Navigate to Settings

From your Dashboard follow the below instructions.

Steps to navigate to settings

  1. 1. Make sure you are in dashboard.
  2. 2. In your left sidebar, click on the Settings icon (gear symbol) to open the settings menu.
  3. 3. From the settings menu, you can access to your organization settings.
2

Set Primary Color

On the Organizations settings page, scroll down to the Branding section.

Setting the primary color

  1. 1. Locate the Primary Color field
  2. 2. Enter your brand's hex color code (e.g. #1A73E8) or use the color picker
  3. 3. The color will be applied across your white-labeled portal — buttons, navbar, and accents
Tips: Use your brand's primary color fot the best visual consistency across the platform.
3

Create CNAME Records at Your DNS Provider

Log in to your domain DNS provider and create two CNAME records pointing to our platform:

  1. 1. Locate the Primary Color field.
  2. 2. Enter your brand's hex color code (e.g. #1A73E8) or use the color picker.
  3. 3. The color will be applied across your white-labeled portal — buttons, navbar, and accents.
TypeName (Host)Value
TXT_nestsms-verify.yourdomain.comnestsms-verify=abc123xyzxxxxxxxxxxxxxxxx
Warning: Cloudflare users: Do not enable the proxy (orange cloud) on these CNAME records. Set them to DNS only (grey cloud). Proxying will break verification.
4

Enter Frontend CNAME in Settings

Back in Settings → Organizations, scroll to the Domain Configuration section.

  1. 1.In the Frontend URL field, enter the full URL of your frontend CNAME:
  2. text
    https://app.yourdomain.com
  3. 1.In Replace app.yourdomain.com with your actual subdomain and domain
5

Enter Backend CNAME in Settings

In the same Domain Configuration section:

  1. 1.In the Backend URL field, enter the full URL of your API CNAME:
  2. text
    https://app.yourdomain.com
  3. 1.Click Save to generate your verification token
6

Insert TXT Record for Domain Verification

After saving, the platform generates a unique TXT verification token for your domain.

  1. 1.Copy the generated token shown on the settings page — it will look like:
  2. text
    nestsms-verify=abc123xyzxxxxxxxxxxxxxxxx
  3. 1.Go back to your DNS provider and add a TXT record:

TypeName (Host)Value
TXT_nestsms-verify.yourdomain.comnestsms-verify=abc123xyzxxxxxxxxxxxxxxxx
Important: Both webhook URLs must use HTTPS. Twilio will not accept HTTP endpoints. Ensure your SSL certificate is active before saving these values.
7

Wait for Verification

After adding the TXT record, the platform will automatically check for it every few minutes.

  • DNS propagation can take 15 minutes to 48 hours depending on your provider.
  • The status badge on the Organizations settings page will update:
    • Pending - verification not yet confirmed
    • Verified - domain ownership confirmed
Tip: You can check DNS propagation progress at whatsmydns.net — search for your _nestsms-verify TXT record.
8

Visit Your Custom Domain

Once your domain shows Verified, open a browser and navigate to your frontend URL:

bash
https://app.yourdomain.com

The platform should load with your organization's branding applied.

9

Upload Favicon & Brand Logo

Favicon
  • Recommended size: 32×32 px or 64×64 px
  • Accepted formats: .ico, .png
  • Appears in the browser tab
Brand Logo
  • Favicon Brand Logo Recommended format: SVG or PNG (transparent background)
  • Max file size: 500 KB
  • Displayed in the top navigation bar of your white-labeled portal
  • Recommended dimensions: height 40px, width proportional
10

You're Live! 🎉

Your white-label platform is fully set up. Here's a summary of what you've configured:

SettingValue
Frontend URLhttps://app.yourdomain.com
Backend URLhttps://api.yourdomain.com
Domain Status✅ Verified
Primary Color✅ Set
Brand Logo✅ Uploaded
Favicon✅ Uploaded

Your clients can now access the platform through your custom domain with your full branding applied.

Troubleshooting

IssuePossible CauseSolution
Domain stuck on PendingDNS not propagated yetWait up to 48h; check at whatsmydns.net
CNAME not resolvingCloudflare proxy enabledSet CNAME to DNS only (grey cloud)
TXT record not foundWrong record nameEnsure name starts with _nestsms-verify.
Logo not showingFile too large or wrong formatUse SVG/PNG under 500 KB
Site loads but no brandingCache issueHard refresh (Ctrl+Shift+R) or clear browser cache

Need Help?

Contact support at support@nestsms.com or open a ticket from your dashboard.

Send SMS through UI

Log In to Dashboard

Sign in to your NestSMS dashboard with your account credentials and open the messaging workspace.

Compose a Message

Go to the send page, enter your sender name, and write the message you want to deliver.

Select Recipients

Add numbers manually, upload a list, or choose a saved group, then review and send.

Bulk SMS File Format Guide

Everything you need to upload contacts and send personalized messages via CSV & XLSX

CSV SupportedXLSX / XLS SupportedTemplate Variables

📄 Supported Formats

FormatExtensions
CSV (Comma-Separated Values).csv
Excel Workbook.xlsx.xls

Required Column: Phone Number

Your file must contain a phone number column. The header can be any of the following (case-insensitive):

  • phone
  • phone_number
  • mobile
  • mobile_number
  • cell
  • cell_number
  • telephone
  • contact
  • contact_number
  • number
  • recipient_phone
  • recipient_number
  • to

Valid Phone Number Formats

All numbers must be Nepali mobile numbers (10 digits starting with 98 or 97).

Input FormatAccepted?
9841234567✓ Yes
+9779841234567 — prefix stripped automatically✓ Yes
9779841234567 — prefix stripped automatically✓ Yes
01-123456 (landline)✗ Skipped
984123 (too short)✗ Skipped

ℹ Note: Rows with invalid or empty phone numbers are silently skipped and reported in the response as skipped_count.

Optional Column: Name

If a name column is present, it is attached to each message as the recipient name. Any of these headers are accepted:

  • name
  • full_name
  • first_name
  • recipient_name
  • customer_name
  • contact_name

Template Variables

Any column in your file can be used as a placeholder in your message template using {columnName} or {{columnName}} syntax.

Column headers are lowercased and spaces replaced with underscores. So First Name{first_name}.

Common Aliases

Template VariableAlso Resolved From
{name}first_name, full_name, firstname, fullname
{firstname}first_name
{lastname}last_name
{fullname}full_name, name, first_name
{phone}phone_number, mobile, mobile_number, contact_number
{mobile}mobile_number, phone, phone_number
{email}email_address

Tip: If a placeholder has no matching column in the row, it is left as-is in the message.

Examples

1. Simple — no variables

Every recipient gets the same message.

File

phone 9841234567 9867654321

Message Template

Your order has been placed.

Result

9841234567 → Your order has been placed. 9867654321 → Your order has been placed.

2. Personalized with name

Each recipient gets a personalized greeting using {name}.

File

phone,name 9841234567,Rishav 9867654321,Sita

Message Template

Hello {name}, your OTP is 4521.

Result

9841234567 → Hello Rishav, your OTP is 4521. 9867654321 → Hello Sita, your OTP is 4521.

3. Multiple custom variables

Payment reminders with amount and due date per recipient.

CSV

phone,name,amount,due_date 9841234567,Rishav,5000,2025-04-01 9867654321,Sita,3200,2025-04-05

💬 Template

Dear {name}, your payment of Rs.{amount} is due on {due_date}.

Result

9841234567 → Dear Rishav, your payment of Rs.5000 is due on 2025-04-01. 9867654321 → Dear Sita, your payment of Rs.3200 is due on 2025-04-05.

Rules & Limits

Header Row First

The first row must always be the header row.

First Sheet Only

Only the first sheet is read from XLSX/XLS files.

Invalid Rows Are Skipped

Rows with invalid phone numbers are skipped — they do not cause the entire batch to fail.

No Hard Row Limit

No hard row limit enforced by the parser, but your account's wallet balance must cover all valid recipients.

Case-Insensitive Headers

Phone, PHONE, and phone all work identically.

Contact Import File Format Guide

Upload your contacts in CSV or Excel format — fast, flexible, and forgiving

CSV / XLSX / XLSUp to 50 MBNepali Numbers

Supported File Types

FormatExtension
CSV (Comma-Separated Values).csv
Excel Workbook.xlsx
Excel 97-2003 Workbook.xls

File Size Limit: Maximum file size is 50 MB

Required Columns

Your file must contain both of the following columns. Column names are case-insensitive.

Name

REQUIRED
  • name
  • full_name
  • first_name
  • recipient_name
  • customer_name
  • contact_name

Phone

REQUIRED
  • phone
  • phone_number
  • mobile
  • mobile_number
  • cell
  • cell_number
  • telephone
  • contact
  • contact_number
  • number
  • recipient_phone
  • to

ℹ️ Note: Any other columns in the file are ignored and will not cause errors.

Phone Number Format

All phone numbers must be valid Nepali mobile numbers.

FormatExampleValid?
10 digits, starts with 989841234567✓ Valid
10 digits, starts with 979741234567✓ Valid
With +977 country code+9779841234567✓ Valid
With 977 (no +)9779841234567✓ Valid
Less than 10 digits984123✗ Invalid
Does not start with 97 or 989541234567✗ Invalid
Contains letters or symbols984-123-4567✗ Invalid

Auto-Strip: The +977 / 977 prefix is automatically stripped before saving.

CSV Example

Basic CSV

name,phone Ram Bahadur,9841234567 Sita Sharma,9801112233 Hari Prasad,+9779751234567 Krishna Thapa,9861111111

With extra columns (they'll be ignored)

name,phone,email,address Ram Bahadur,9841234567,ram@example.com,Kathmandu Sita Sharma,9801112233,sita@example.com,Pokhara

Excel Example

namephone
Ram Bahadur9841234567
Sita Sharma9801112233
Hari Prasad9751234567

Important: For Excel files, data must be on the first sheet. Other sheets are ignored.

What Gets Skipped

Rows are silently skipped (counted in "Skipped") if any of the following apply:

Blank Row

The row is completely empty

Empty Name

The name field is missing or blank

Invalid Phone

Phone number is missing or in an invalid format

Duplicate (Existing)

Phone already exists in your contacts

Duplicate (In File)

Same number appears twice — only the first occurrence is kept

Import Result

After a successful import, you will see:

Created

Number of new contacts successfully added to your list

Skipped

Rows not imported, with reasons shown per row (up to 10 displayed)

Quick Checklist

Check each item before uploading your file:

File is .csv, .xlsx, or .xls
First row is a header row
A name column exists (or any accepted alias)
A phone column exists (or any accepted alias)
Phone numbers are 10-digit Nepali mobile numbers (97 or 98 prefix)
File size is under 50 MB