Let’s get you live with lightning fast OTPs!

We’ll take you from “just signed up” to “sending OTPs like a pro” in a few quick steps.

Verify Now API

OTP SMS API Documentation

Version – 3.0
tick-icon

 Step 1: Sign Up & Log In 

  1. Go to Message Central Signup
  2. Create your free account 
  3. Use your free credits to test and play around with OTPs
📌 Tip: Use your free credits to send test OTPs right away before going live.
tick-icon

Step 2: API Integration (Devs, Assemble!)

We’ve kept things super simple. Copy a few code snippets and you’ll be testing in no time.

Prefer watching over coding? Our quick video guide has you covered.

API Parameter for Verify Now

The following parameters need to be sent while using VerifyNow APIs.

API Parameter
Type
Value
flowType
String
SMS/WHATSAPP/
RCS/SAUTH
type
String
OTP

Rest API Base URLs

All Platform API endpoints below should be prefixed with the following URL:

https://cpaas.messagecentral.com

Generate Token

When using  Verify Now’s SMS verification  API to send SMS verification codes, the initial call should be to the token generation API.

This API returns a token that must be included in all subsequent calls. An authentication token is needed to validate the user and should be included in the header section of each request.

Field
Type
Mandatory?
Description
customerId
String
yes
Customer identifier (need to signup to get your  customer id)
country
String
no
Country code to send OTP to
email
String
no
Email
key
String
yes
Base-64 encrypted password
scope
String
no
Use ‘NEW’ for first time

Request URL Path:

/auth/v1/authentication/token

cURL

1curl --location 'https://cpaas.messagecentral.com/auth/v1/authentication/token? 
2customerId=%3CCustomerId%3E&key=%3CBase64%20Encrypted%20password%3E&scope=NEW&country=91
3&email=test%40messagecentral.com' \
4--header 'accept: */*'

NOTE: To convert a cURL command into code using Postman, open Postman, import the cURL command via the "Import" button, and then generate the code in your preferred language by clicking the "Code" button on the right side of the request.

Response JSON

1{
2  "status": Integer,
3  "token": "String"
4}

Code Example

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
  .url("https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com")
  .method("GET", body)
  .addHeader("accept", "*/*")
  .build();
Response response = client.newCall(request).execute();
var request = require('request');
var options = {
  'method': 'GET',
  'url': 'https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com',
  'headers': {
    'accept': '*/*'
  }
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'accept' => '*/*'
));
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}
import requests

url = "https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com"

payload = {}
headers = {
  'accept': '*/*'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)
require "uri"
require "net/http"

url = URI("https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Get.new(url)
request["accept"] = "*/*"

response = https.request(request)
puts response.read_body

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://cpaas.messagecentral.com/auth/v1/authentication/token?customerId=<CustomerId>&key=<Base64 Encrypted password>&scope=NEW&country=91&email=test@messagecentral.com");
request.Headers.Add("accept", "*/*");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());

Send OTP

To sendOtp  on a mobile number below are the request parameters. The authentication token is required to send OTP which is generated by the generated token API (which you can find above in Introduction section).

API Parameter
Type
Mandatory?
authToken
String
Yes

Request URL Path:

A successful response will return a 200 status code.

/verification/v3/send

Request URL Parameters:

Field
Type
Mandatory?
Description
customerId
String
yes
Country code
otpLength
Integer
no
Send a number between 4 and 8. Default is 4
mobileNumber
String
yes
Mobile number for single text
flowType
String
yes
We send OTP using multiple mediums like SMS, WhatsApp, email, etc. For now, use either SMS or WhatsApp

cURL

1curl --location --request POST 'https://cpaas.messagecentral.com/verification/v3/send? 
2countryCode=91&flowType=SMS&mobileNumber=9999999999' \
3--header 'authToken: 
4eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDIGNzQwNCI6ImIhdCI6MTcxMjExOTA0MCwiZXhwIjo'

NOTE: To convert a cURL command into code using Postman, open Postman, import the cURL command via the "Import" button, and then generate the code in your preferred language by clicking the "Code" button on the right side of the request. You can change the flowType basis the channel of your choice.

Response JSON

1{
2  "responseCode": 200,
3  "message": "SUCCESS",
4  "data": {
5    "verificationId": "xxxx",
6    "mobileNumber": "xxxx",
7    "responseCode": "200",
8    "errorMessage": null,
9    "timeout": "60",
10    "smCLI": null,
11    "transactionId": "xxxx"
12  }
13}

Code Example

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
  .url("https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999")
  .method("POST", body)
  .addHeader("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw")
  .build();
Response response = client.newCall(request).execute();
var request = require('request');
var options = {
  'method': 'POST',
  'url': 'https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999',
  'headers': {
    'authToken': 'eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw'
  }
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'authToken' => 'eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw'
));
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}
import requests

url = "https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999"

payload = {}
headers = {
  'authToken': 'eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
require "uri"
require "net/http"

url = URI("https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["authToken"] = "eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw"

response = https.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999");
request.Headers.Add("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());

Validate OTP

The  validateOtp method is a REST API endpoint for validating a one-time password (OTP) for customers.

Request Header
Type
Mandatory?
authToken
String
Yes

Request URL Path:

A successful response will return a 200 status code.

/verification/v3/validateOtp/
Field
Type
Mandatory?
Description
verificationId
Long
yes
VerificationId from response of  /send  api
code
String
yes
otp
langid
String
no
Mobile number for single text
flowType
String
yes
  • For multiple language support
  • by default is English
  • For now we support English only

cURL

1curl --location 'https://cpaas.messagecentral.com/verification/v3/validateOtp? 
2&verificationId=2949&code=1476' \
3--header 'authToken: 
4eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDIGNzQwNCI6ImIhdCI6MTcxMjExOTA0MC'

NOTE: To convert a cURL command into code using Postman, open Postman, import the cURL command via the "Import" button, and then generate the code in your preferred language by clicking the "Code" button on the right side of the request.

Response JSON

A successful response will return a 200 status code.

1{
2  "responseCode": 200,
3  "message": "SUCCESS",
4  "data": {
5    "verficationId": "xxxx",
6    "mobileNumber": "xxxx",
7    "responseCode": "200",
8    "errorMessage": null,
9    "verificationStatus": "VERIFICATION_COMPLETED",
10    "authToken": null,
11    "transactionId": "xxxx"
12  }
13}

Code Example

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
  .url("https://cpaas.messagecentral.com/verification/v3/validateOtp?&verificationId=2949&code=1476")
  .method("GET", body)
  .addHeader("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOT")
  .build();
Response response = client.newCall(request).execute();
var request = require('request');
var options = {
  'method': 'GET',
  'url': 'https://cpaas.messagecentral.com/verification/v3/validateOtp?&verificationId=2949&code=1476',
  'headers': {
    'authToken': 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOT'
  }
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://cpaas.messagecentral.com/verification/v3/validateOtp?&verificationId=2949&code=1476');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'authToken' => 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOT'
));
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://cpaas.messagecentral.com/verification/v3/validateOtp?&verificationId=2949&code=1476");
request.Headers.Add("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOT");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());

require "uri"
require "net/http"

url = URI("https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999")

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["authToken"] = "eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw"

response = https.request(request)
puts response.read_body
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://cpaas.messagecentral.com/verification/v3/send?countryCode=91&flowType=SMS&mobileNumber=9999999999");
request.Headers.Add("authToken", "eyJhbGciOiJIUzUxMiJ9.eyJzdWOiJDLTMzNDMyQTVGNDlGNzQwNCIsImlhdCI6MTcxMjExOTA0MCwiZXhw");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());

Response Codes

Code
Display Text
200
SUCCESS
400
BAD_REQUEST
409
DUPLICATE_RESOURCE
500
SERVER_ERROR
501
INVALID_CUSTOMER_ID
505
INVALID_VERIFICATION_ID
506
REQUEST_ALREADY_EXISTS
511
INVALID_COUNTRY_CODE
700
VERIFICATION_FAILED
702
WRONG_OTP_PROVIDED
703
ALREADY_VERIFIED
705
VERIFICATION_EXPIRED
800
MAXIMUM_LIMIT_REACHED

Need Help?

Need a hand? Our experts are available 24/7 to guide you through anything, anytime.

Support email: support@messagecentral.com  

Frequently Asked Questions

1. How can I implement with custom code?

You can use postman to rewrite API calls in your preferred programming language. You’d need to use: -

  • POST method for sending SMS
  • GET method for validating OTP SMS

2. Why am I getting the error “Method not Allowed” or error code 405?

You’d need to ensure that the endpoint URL for the Token API matches the one defined in the documentation.The same has been mentioned below: -

a. Token API using GET method

b. Send API using POST method

c. Validate API using GET method



3. How do I test the SMS verification APIs?

You can use Postman to test Message Central’s SMS verification APIs.

4. Why am I getting “Whitelabel Error” page?

If you are getting Whitelabel Error page, you should check for the following errors: -

a. You are using incorrect API packet

b. You have not replaced placeholders with the actual values

c. API curl is incorrect

d. Curl location has not been changed to ‘production’ instead of ‘staging’

5. Why am I getting error code 401?

This could be because of incorrect API or token. Make sure you have followed our API documentation for the right token values.

6. Why am I getting the error code 400 or “Bad Request” in Postman?

There are multiple reasons for a “Bad Request” in Postman. You can check for the following probable reasons: -

a. Check if the authToken is correct

b. Check if any parameter is missing in API

c. Header should be passed in this format - application-x-www-form-urlencoded

7. How do I generate authToken?

In order to generate the authToken in Postman, you’d:-

a. Find a ‘key’ in GET API

b. You’d need to copy that key and encode the same using Base64 - https://www.base64encode.org/

c. Put the encoded password in ‘Key’ placeholder

d. Hit the GET API

You’d get an authToken.

8. Where do I use the authToken?

You’d need to put the authToken in the header of SEND API.

9. What all placeholders do I need to change in GET API to generate token?

You’d need to change country, customerid, email and key. Scope will always remain = “NEW”

Field
Type
Mandatory?
Description
customerId
String
yes
Customer identifier (need to signup to get your  customer id)
key
String
yes
Base-64 encrypted password

10. What all placeholders do I need to change in SEND API?

You’d need to change country code, customerid, otplength, and mobilenumber.

Field
Type
Mandatory?
Description
customerId
String
yes
Country code
flowType
String
yes
We send OTP using multiple mediums like SMS, WhatsApp, email, etc. For now, use either SMS or WhatsApp

What is the best SMS OTP API for India based on delivery reliability and latency

Look for an SMS OTP API provider that offers direct operator routes, regional carrier peering, automated retry/fallback, and measurable delivery SLAs. Best-in-class providers also support DLT compliance, delivery receipts (DLR), low-latency routing, and SMS + WhatsApp fallback. Evaluate by measuring real-world delivery rates, average latency (ms), and error breakdowns across major Indian carriers (Jio, Airtel, Vi).

How fast should OTP delivery be for Indian apps, and what impacts latency?

Aim for sub-2 seconds delivery for best UX (under 5s acceptable). Latency is affected by carrier routing, queueing during peak traffic, network congestion, SMS aggregator hops, and DLT/template delays. Use direct routes, parallel routing, and in-built SMS + WhatsApp fallback to minimise end-to-end OTP latency and boost completion rates.

How do I integrate an SMS OTP API into my app in India (Node.js, Python, PHP, Java)?

Basic flow: generate secure OTP → call provider’s send-OTP API → store OTP hash + expiry → verify user input against stored hash via verify-OTP API/webhook. Most providers offer REST endpoints and OTP Verification SDKs. Example (pseudo):
Node.js (fetch):
await fetch('https://api.provider/send', {  
method:'POST',  
headers:{'Authorization':'Bearer KEY','Content-Type':'application/json'},  
body: JSON.stringify({to:'+91XXXXXXXXXX', template:'OTP {{code}}',
variables:{code:123456}}) });
Use HTTPS, HMAC/signing, retries, and webhooks for delivery receipts (DLR).

How do I prevent OTP resend abuse or brute-force attempts in my authentication flow?

Implement server-side protections:

  • Limit resend attempts per phone per time window (e.g., 3 sends / 10 minutes).
  • Rate-limit verification attempts and add progressive delays or temporary blocks after failures.
  • Using the staginUse device + IP fingerprinting and CAPTCHAs for suspicious flows.g endpoint instead of production
  • Lock account or require secondary verification after N failed attempts. Log attempts and notify security teams.

What causes OTP delivery failures in India and how do developers fix them?

Common causes: DLT/template rejection, carrier filtering (DND), poor routing, incorrect sender ID, number formatting issues, or temporary operator outages. Fixes: ensure DLT & template approvals, use correct E.164 phone format (+91), retry with alternate routes, enable SMS fallback, monitor DLRs, and use a provider with direct carrier relationships.

How do I test OTP APIs in sandbox mode without sending real SMS?

Use the provider’s sandbox or test keys to emulate sends/receipts. Sandbox features usually return simulated DLRs and verification responses. Locally, mock API responses for unit tests and use staging credentials for end-to-end QA. Always validate webhooks in staging (use tools like ngrok) and log all events to replay in tests.

How do I implement rate limiting and retry logic for OTP delivery?

Rate limiting: enforce per-phone and per-IP limits at API gateway (e.g., 3 sends per 10 min). Retry logic: implement exponential backoff for transient errors and immediate retry via alternate route for soft failures. Always cap retries (e.g., 3 attempts) and track retry outcomes to avoid spam or carrier blocks.

Can I use alphanumeric Sender ID for OTP in India?

No — India requires numeric Sender IDs via DLT-approved routes for most transactional flows; alphanumeric is generally not allowed for OTPs. OTPs must follow telecom/DLT rules. Use a compliant provider that manages DLT registration and numeric Sender ID setup for you.

What is the recommended OTP expiry time and retry cycle for Indian users?

Expiry: 3–5 minutes is standard for transactional OTPs (2FA). Retry cycle: allow 1–2 immediate resends with anti-abuse limits (e.g., max 3 sends per 10–15 minutes). Short expiries reduce fraud; sensible resend limits reduce support load and operator filtering.

What is the difference between Transactional vs Service Implicit OTP routes?

Transactional routes are for critical, expected messages (OTP, order updates) that follow stricter compliance and usually have higher delivery priority. Service Implicit (or other service routes) can vary by operator and often apply different template/consent rules. Check your provider’s routing docs — choose transactional routes for OTPs to maximise deliverability and compliance.

How do I migrate from another SMS provider to a new OTP API with zero downtime?

Plan a phased cutover:

  • Configure new provider and provision DLT/templates in parallel.
  • Mirror traffic (send test batches) and verify delivery metrics.
  • Implement dual-sending for a small percentage of live traffic (A/B), monitor results.
  • Gradually shift traffic while keeping old provider as fallback.
  • Update DNS/webhook endpoints, revoke old keys once stable. Monitor logs and delivery rates closely.

Ready to get started?

Safeguard your user accounts and build trust with your customers using SMS verification. Try Verify Now without any credit card.