Cash on Delivery OTP Verification: Cut E-Commerce CoD Fraud by 80%
Indian e-commerce brands lose millions to CoD returns. Learn how implementing a two-layer OTP verification flow cuts cash-on-delivery fraud by up to 80%.
Ankit’s D2C clothing brand, shipping out of Surat, was celebrating a record week—15,000 orders in six days. But by the following Monday, the festive mood turned into a financial headache: 40% of their Cash on Delivery (CoD) orders had bounced back as returns. Non-existent addresses, wrong mobile numbers, and customers simply refusing the parcel at their doorstep. Between double courier fees and blocked inventory, the brand lost ₹18 lakh in returns in a single month.
For e-commerce founders and product managers in India, this scenario is all too familiar. While digital payment methods like UPI have seen explosive growth, Cash on Delivery (CoD) remains the primary checkout choice for 35% to 50% of consumers. This preference is driven by a deep-rooted consumer desire to inspect physical products before releasing capital. However, this payment mode creates a structurally unique operational risk: CoD return fraud. Without proper confirmation controls, brands end up shipping physical products to unverified addresses, incurring heavy shipping and return shipping fees (RTO - Return to Origin) that drain their operating margins.
The CoD Problem Nobody Talks About Honestly
In the Indian e-commerce landscape, transaction volumes are heavily supported by buyers in Tier 2, Tier 3, and rural markets. Large players like Meesho, Flipkart, Amazon India, and Myntra maintain massive CoD order books because a large segment of Indian buyers will not prepay. The primary issue with CoD transactions is not the payment type itself, but the lack of commitment it requires from the buyer. A shopper can place an order with a single click, but the brand bears 100% of the financial risk of fulfillment.
If a prepaid customer cancels an order or refuses delivery, the brand has already collected the funds to cover shipping, handling, and inventory holding costs. In contrast, when a CoD customer declines a shipment, the brand loses the outward shipping fee, pays an additional return-to-origin fee to the logistics partner, and holds inventory in transit for 7 to 15 days. This sequence reduces the resale value of seasonal items and strains cash flows.
┌──────────────────────────────────────────────────────────────────┐
│ The Financial Impact of CoD Returns │
└──────────────────────────────────────────────────────────────────┘
[Prepaid Transaction] [CoD RTO Transaction]
┌───────────────────────┐ ┌───────────────────────┐
│ Capture Payment │ │ Prepare & Pack Order │
└──────────┬────────────┘ └──────────┬────────────┘
│ │ (Ship Out)
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Pack & Deliver Parcel │ │ Customer Rejects RTO │
└───────────────────────┘ └──────────┬────────────┘
Result: Revenue Earned │ (Ship Back)
▼
┌───────────────────────┐
│ Pay Outward + Return │
│ Shipping & Handle Fees│
└───────────────────────┘
Result: Net Loss on Order
This structural loss is driven by four primary types of Cash on Delivery fraud that occur across Indian marketplaces:
1. Fake Number Fraud
Shoppers enter incorrect or random mobile numbers during checkout to bypass verification or speed up purchase forms. Because carriers cannot confirm these numbers, delivery agents cannot reach the buyer when trying to deliver the parcel. The package is marked as undeliverable, and the brand pays the shipping fees both ways.
2. Impersonation Fraud
In this pattern, a bad actor uses a victim’s real mobile number and address to send unwanted items to their doorstep. When the delivery agent arrives, the victim refuses the parcel, stating they never placed the order. The merchant is left to cover the logistics costs.
3. Address Manipulation Fraud
Some buyers enter a slightly modified or incorrect address to redirect orders to a reseller or bypass area delivery restrictions. If the delivery agent fails to locate the buyer, the order is returned.
4. Organised Return Fraud
In more coordinated schemes, repeat offenders order high-value items via CoD, accept delivery, open the packaging, swap the premium product for a defective alternative, and request a return. Without delivery-handover verification, the merchant struggles to prove the original product was delivered in good condition.
The Cost of Returns in Numbers
Let’s look at the financial impact in rupees. Consider a mid-sized D2C brand generating a monthly Gross Merchandise Value (GMV) of ₹10 crore. If CoD orders make up 35% of their volume (₹3.5 crore) and the brand faces a typical 10% return rate on those orders, they deal with ₹35 lakh worth of failed deliveries every month.
With average shipping and return fees totaling ₹150 per package, processing 10,000 returned shipments costs the brand ₹15 lakh in pure logistics losses, plus the cost of capital tied up in transit.
In contrast, implementing an OTP verification flow to validate those 10,000 transactions before shipping costs approximately ₹2,500 (at ₹0.25 per SMS OTP). This represents a minor expense that can save millions in RTO costs.
The Two-Layer OTP Architecture That Indian E-Commerce Leaders Use
To build a secure cash-on-delivery process, e-commerce brands should use a two-layer verification architecture. Confirming orders only at checkout is not enough. Verification must occur at two critical points: during order placement (checkout) and during delivery handover (doorstep).
Layer 1: Order Confirmation OTP (At Placement)
The first verification layer occurs immediately after the user selects Cash on Delivery on the checkout screen. Before the order is sent to the warehouse or ERP system for packaging, the application must verify the buyer’s phone number.
The system triggers a transactional SMS containing the order details and a secure verification code. The message must follow a DLT-approved transactional template:
Your order #109283 for Rs.4,500 has been placed on BrandName. Confirm with OTP 882910 to proceed. Do not share. Valid 30 min.
[Checkout: CoD Chosen] ────> [Send OTP via StartMessaging] ────> [Customer Verifies]
│
┌─────────────────────────────────── Dismiss ─────────────────────────┘
▼
[24-Hour Timeout Window]
│
├─> [Within 30 Mins] ──> Auto-Promoted to Warehouse
├─> [After 30 Mins] ──> Send WhatsApp Reminder Prompt
└─> [After 24 Hours] ──> Auto-Cancel & Send Reorder SMS Link
The system should give the customer 30 minutes to enter this OTP on the order confirmation screen. If the customer closes the screen or misses the message, the order status is set to pending_confirmation.
If the order is not verified within 30 minutes, the backend triggers a automated reminder via WhatsApp or SMS. If the customer does not verify the transaction within 24 hours, the order is marked as auto_cancelled, the inventory is released, and a reorder link is sent to the customer.
Allowing a 24-hour verification window is essential. If you auto-cancel orders too quickly, you risk losing legitimate buyers who were traveling or busy when the order was placed.
Based on industry data, roughly 7% of CoD orders remain unconfirmed after 24 hours. The breakdown of these unconfirmed orders reveals:
- 60% are fake or duplicate numbers (correctly filtered out, saving return shipping costs).
- 30% are genuine buyers who forgot to confirm but will reorder when they receive the cancellation notice and link.
- 10% are lost customers who changed their mind.
Filtering out these high-risk orders before they enter the shipping queue saves significant fulfillment and RTO costs.
Layer 2: Delivery Confirmation OTP (At Handover)
The second verification layer occurs at the customer’s doorstep. This layer prevents buyers from claiming they never received the package and protects against delivery agents falsely marking parcels as delivered.
When the shipping partner’s delivery agent arrives at the customer’s address, they request a delivery verification code. This code is sent to the customer’s phone number when the package is marked “Out for Delivery” by the courier partner.
The message must follow this template:
Your BrandName order #109283 is out for delivery. Show OTP 339201 to receive your parcel. Valid 60 min.
The delivery agent enters the customer’s OTP into their mobile application to complete the delivery in their system. This code acts as a digital signature, proving the package was handed to the right person.
Major Indian logistics providers—including Delhivery, Shadowfax, Blue Dart, Ekart, and J&T Express—support delivery verification APIs. Brands can send this doorstep OTP using StartMessaging’s API and share the code with the logistics provider’s system. The delivery agent’s app will then require this exact code to complete the handover.
This double-OTP system protects against fraudsters who confirm the initial order using temporary numbers, as they must still be physically present to receive the delivery.
DLT Template Registration for CoD OTP
To send transactional messages in India, you must register your templates under the Distributed Ledger Technology (DLT) framework mandated by the Telecom Regulatory Authority of India (TRAI). Because CoD verification messages contain dynamic data (such as order IDs, amounts, and brand names), they require a specific DLT template design.
For more details on TRAI rules, see our promotional vs. transactional SMS guide.
┌──────────────────────────────────────────────────────────┐
│ DLT Template Registration Flow │
└──────────────────────────────────────────────────────────┘
[Define Template] ──> [Apply DLT Variables] ──> [Submit to Carrier]
│
┌─────────────────────── Rejection ────────────────────┘
▼
• Message includes unapproved URLs
• Too many variables used (Max 4-5 recommended)
• Template lists competitor brand names
• Content categorized as promotional instead of transactional
The Transactional Template Format
To pass carrier DLT approval for order confirmation, your template must define dynamic values using the standard {#var#} placeholder. The rest of the message must remain static.
A DLT-approved template format looks like this:
Your order #{#var#} for Rs.{#var#} has been placed on {#var#}. Confirm with OTP {#var#} to proceed. Do not share. Valid 30 min.
When submitting this template to DLT portals run by Jio, Airtel, or Vi, use the Service Implicit (Transactional) category. This category allows your messages to bypass Do Not Disturb (DND) registries, ensuring the code is delivered to all customers.
Common Rejection Reasons for CoD Templates
DLT approvals are managed by carrier carrier systems, and templates are often rejected for three common reasons:
- Including Unregistered URLs: If you include a link (like a tracking page or reorder link) in a transactional template, the URL must be pre-registered and use an approved domain. Transactional OTP templates should not contain URLs.
- Using Too Many Variables: Avoid using more than 4 or 5 variables in a single template. If a template has too many placeholders, carrier filters may flag it as promotional spam.
- Vague Brand Identifiers: Your template must include your registered DLT Header (Sender ID) or explicitly name your brand. Using generic terms like “Store” or “Brand” will lead to rejection.
The DLT registration process typically takes 2 to 24 hours. If you want to bypass this process and go live immediately, you can use StartMessaging’s pre-approved transactional routing. This allows you to send confirmation codes using our pre-registered DLT headers, removing the need for manual setup.
Implementation Guide: CoD OTP Flow in Node.js
To implement this verification flow, your backend must generate the verification code, store it securely in a caching database (like Redis) with a Time-to-Live (TTL) index, send the SMS using the StartMessaging API, and verify the user’s input.
┌─────────────────────────────────┐
│ E-Commerce Backend │
└────────┬──────────────┬─────────┘
│ │
(1) Generate OTP │ │ (2) Store in Cache
▼ ▼
┌────────────┐ ┌───────────┐
│ Axios API │ │ Redis DB │
└──────┬─────┘ └───────────┘
│
▼ (3) SMS Delivery
┌────────────┐
│ Customer │
└────────────┘
The Node.js implementation below uses Express, Redis, and Axios to manage the order confirmation flow:
const express = require('express');
const axios = require('axios');
const redis = require('redis');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Initialize Redis Client for OTP caching
const redisClient = redis.createClient({ url: process.env.REDIS_URL });
redisClient.connect().catch(console.error);
// StartMessaging Configuration
const STARTMESSAGING_API_URL = 'https://api.startmessaging.com/v1/sms/send';
const API_KEY = process.env.STARTMESSAGING_API_KEY;
const DLT_SENDER_ID = 'STMSG'; // Your DLT approved Header
/**
* Endpoint triggered when a customer places a Cash on Delivery order
*/
app.post('/api/orders/place-cod', async (req, res) => {
const { orderId, amount, phoneNumber, brandName } = req.body;
if (!orderId || !amount || !phoneNumber) {
return res.status(400).json({ success: false, error: 'Missing order parameters.' });
}
try {
// 1. Generate a secure 6-digit OTP code
const otpCode = crypto.randomInt(100000, 999999).toString();
// 2. Save OTP to Redis with a 30-minute (1800s) expiration window
const cacheKey = `cod-otp:${orderId}`;
await redisClient.set(cacheKey, JSON.stringify({
otpCode,
phoneNumber,
amount,
attempts: 0
}), { EX: 1800 });
// 3. Send the OTP using the StartMessaging API
const response = await axios.post(STARTMESSAGING_API_URL, {
to: phoneNumber,
sender: DLT_SENDER_ID,
message: `Your order #${orderId} for Rs.${amount} has been placed on ${brandName}. Confirm with OTP ${otpCode} to proceed. Do not share. Valid 30 min.`,
templateId: '1207161829038491823' // DLT Template ID
}, {
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }
});
return res.status(200).json({
success: true,
message: 'OTP dispatched successfully.',
requestId: response.data.requestId
});
} catch (error) {
console.error('Failed to dispatch CoD verification OTP:', error.message);
return res.status(500).json({ success: false, error: 'Failed to process order confirmation.' });
}
});
/**
* Endpoint called when the user enters the OTP code
*/
app.post('/api/orders/verify-cod', async (req, res) => {
const { orderId, otpSubmitted } = req.body;
if (!orderId || !otpSubmitted) {
return res.status(400).json({ success: false, error: 'Missing parameters.' });
}
const cacheKey = `cod-otp:${orderId}`;
try {
const cachedData = await redisClient.get(cacheKey);
if (!cachedData) {
return res.status(404).json({ success: false, error: 'Verification window expired. Request new OTP.' });
}
const { otpCode, phoneNumber, amount, attempts } = JSON.parse(cachedData);
// Rate-limiting: Max 3 verification attempts per order
if (attempts >= 3) {
await redisClient.del(cacheKey);
// Trigger order cancellation logic
await cancelOrder(orderId, phoneNumber, 'Max verification attempts exceeded.');
return res.status(400).json({ success: false, error: 'Too many attempts. Order canceled.' });
}
// Verify OTP code
if (otpCode !== otpSubmitted) {
// Increment attempt counter in Redis
await redisClient.set(cacheKey, JSON.stringify({
otpCode, phoneNumber, amount, attempts: attempts + 1
}), { KEEPTTL: true });
return res.status(400).json({ success: false, error: 'Invalid verification code.' });
}
// Successful confirmation: Promote order to fulfillment
await redisClient.del(cacheKey);
await promoteToWarehouse(orderId);
return res.status(200).json({ success: true, message: 'Order verified and sent to fulfillment.' });
} catch (error) {
console.error('OTP verification error:', error.message);
return res.status(500).json({ success: false, error: 'Internal system error.' });
}
});
async function promoteToWarehouse(orderId) {
// Update order status in main database
console.log(`Order ${orderId} successfully promoted to warehouse.`);
}
async function cancelOrder(orderId, phoneNumber, reason) {
// Update order status to canceled
console.log(`Order ${orderId} canceled for reason: ${reason}`);
}
app.listen(3000, () => console.log('CoD Order Verification server listening on port 3000'));
This Node.js application provides the endpoints required to verify order confirmations. It uses Redis to manage expiration times and attempt limits, protecting your system against brute-force verification attempts.
To ensure your system runs smoothly, monitor these three key performance indicators:
- Verification Rate (Target: 90%–93%): The percentage of CoD orders successfully verified within 24 hours. A lower rate may indicate network issues or poor form design.
- False Cancellation Rate (Target: less than 3%): The percentage of customers who attempt to reorder after their original order is canceled. If this number is high, consider extending your verification window.
- RTO Rate (Target: less than 5%): Monitor your return-to-origin rates. A successful implementation should reduce returns by 80% within the first month.
Fraud Pattern Benchmarks and What to Expect
When implementing a two-layer verification flow, e-commerce brands typically see a significant drop in return rates within the first 30 days.
┌──────────────────────────────────────────────────────────┐
│ RTO Performance Benchmarks │
└──────────────────────────────────────────────────────────┘
RTO Rate ▲
20% ┼── [Before verification: 12% - 18%]
│
10% ┼
│
3% ┼────────────────── [After implementation: 2% - 4%]
└────────────────────────────────────────────────►
Day 0 Day 30
This performance chart demonstrates how RTO rates decrease as unverified orders are filtered out of the shipping pipeline.
Regional Variations and Network Tuning
In Tier 1 metros (like Mumbai, Bengaluru, and Delhi), network coverage is consistent, and customers verify orders quickly. For metro areas, a 30-minute verification window is standard.
However, in Tier 2 and Tier 3 cities, network congestion can delay SMS delivery. For shipments to smaller towns, extend your verification window to 60 minutes to reduce friction for legitimate buyers.
High-Value Transactions
For high-value orders (above ₹5,000 to ₹10,000, depending on your product category), consider adding a third verification touchpoint if the customer changes their shipping address. If an address is updated after checkout, send a confirmation code to the user’s original phone number to prevent unauthorized redirection.
Data Privacy and DPDP Compliance
Under India’s Digital Personal Data Protection (DPDP) Act, verification records linking phone numbers to order details must be handled carefully. Retaining delivery logs indefinitely is a compliance risk.
We recommend deleting or anonymizing door-step delivery logs after 90 days, which provides enough time to resolve delivery disputes while staying compliant with the law.
For more details on data handling, see our guide on DPDP Act and SMS OTP developer compliance.
Frequently Asked Questions
Q: Do I need DLT registration to send CoD confirmation OTPs?
A: Yes, TRAI regulations require all transactional messages sent to Indian phone numbers to use registered DLT templates and headers. Sending transactional verification codes without DLT registration will cause carriers to block your messages. You can use StartMessaging’s pre-approved routes to bypass this requirement during integration.
Q: What happens to legitimate orders that miss the OTP window?
A: If a user fails to confirm their order within the initial window, the backend sends a reminder via SMS or WhatsApp. If they do not verify the transaction within 24 hours, the order is canceled, and a reorder link is sent to their number. This allows genuine buyers to easily re-verify and place their order again.
Q: Can I use the same OTP template for order confirmation and delivery confirmation?
A: No, because they serve different purposes and contain different details, they require separate DLT templates. The checkout confirmation template includes the order amount and a verification link, while the delivery template is simpler and focuses on the handover code.
Q: Does CoD OTP work for all Indian logistics partners?
A: Yes, major logistics providers in India—including Delhivery, Shadowfax, and Blue Dart—provide APIs to integrate verification codes. You can generate the OTP on your system, send it via StartMessaging, and send the same code to the courier’s system to require it at delivery.
Q: What is the typical CoD fraud reduction after implementing two-layer OTP?
A: Most D2C brands see their RTO and return rates drop by 70% to 80% within the first month. By filtering out incorrect numbers at checkout and requiring doorstep confirmation, you eliminate fake orders and delivery disputes.
Q: How do I handle users whose phone number is different from the person receiving the delivery?
A: If the recipient’s phone number differs from the one registered on the account, the delivery agent can trigger a resend code. The buyer can then forward the OTP to the person receiving the package, or the agent can call the registered account holder to confirm the handover.
Secure Your E-Commerce Pipelines with StartMessaging
Reducing cash-on-delivery returns requires a reliable API partner to ensure your verification codes are delivered instantly. StartMessaging is designed to handle high-volume transactional messages for Indian e-commerce:
- Fast Delivery: Our API routes messages through direct carrier links, ensuring your checkout codes arrive within seconds.
- DLT Management: Use our pre-approved templates to start sending verification codes immediately, avoiding carrier approval delays.
- Secure Caching: Protect your verification endpoints against brute-force attacks and SMS pumping fraud with our built-in rate-limiting tools.
- Analytics Dashboard: Track verification rates, delivery status, and latency metrics across all major Indian networks in real-time.
If you are ready to protect your margins and reduce RTO losses, sign up for a developer account today to configure your verification pipeline.
Ready to protect your margins? Create a free account on StartMessaging to integrate compliant, secure verification APIs today.
StartMessaging Team
StartMessaging Team