<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ravi Rai</title>
    <description>The latest articles on DEV Community by Ravi Rai (@buildbyravirai).</description>
    <link>https://dev.to/buildbyravirai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3894804%2Fae5fb341-149a-41eb-9ebc-292b7b706e8a.jpg</url>
      <title>DEV Community: Ravi Rai</title>
      <link>https://dev.to/buildbyravirai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buildbyravirai"/>
    <language>en</language>
    <item>
      <title>The website that kept Meera's oven warm</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Tue, 21 Jul 2026 06:56:47 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/the-website-that-kept-meeras-oven-warm-49jj</link>
      <guid>https://dev.to/buildbyravirai/the-website-that-kept-meeras-oven-warm-49jj</guid>
      <description>&lt;p&gt;There is a particular quiet that settles into a kitchen when the orders stop coming. Meera noticed it first in the flour tin. She used to go through a ten kilo bag most weeks. That month, one bag lasted almost three. The oven that used to run from early morning sat cold through the afternoons. She would wipe a counter that was already clean, pick up her phone, look at nothing, and put it back down. Then wipe the counter again. Meera is not her real name, and the town does not matter for this. She runs a small bakery out of her own kitchen in a tier-2 city. Cakes for birthdays, boxes of cookies for Rakhi, the occasional wedding order that kept her up for two nights straight and made her proud for a week. What she earns from that oven is not pocket money. It feeds her family. Her husband's work is the kind that pays well one month and almost nothing the next, so the bakery is the floor the whole house stands on. And that year, after the festive rush ended, the floor started to give.&lt;/p&gt;

&lt;h2&gt;
  
  
  The site that said she was closed
&lt;/h2&gt;

&lt;p&gt;When she first messaged us, I did the thing I always do before a call. I looked her up on my phone, the way a hungry stranger would at nine at night. The page took a long time to load. Long enough that in real life the person would have tapped back and gone to the next bakery in the list. When it finally opened, the text was crammed into a thin column shoved to one side of the screen, the photos were stretched out of shape, and there was a phone number sitting in bold near the top. That number had not been hers for two years. Someone had built the page for free a long time ago, taken whatever they were paid, and vanished. There was no menu you could read. No prices. No button to place an order, no chat, nothing. Just one photo of a cake and a dead phone number. Here is the part that stayed with me. To a stranger, that page did not look like a bakery having a hard month. It looked like a bakery that had shut down and forgotten to take its sign off the wall. Every single person who found her online was quietly being told she was closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The week she almost gave up
&lt;/h2&gt;

&lt;p&gt;The festive months had been kind. Diwali orders, a couple of weddings, boxes going out the door every day, the oven barely cooling between batches. Then the season ended, the way it always does, and the slump rolled in behind it. That much she was used to. Every year has a lean stretch, and she had learned to ride it out. What she could not ride was the hospital bill that arrived the same month the orders thinned. I am not going to put the figure here. It was the kind of number that does not leave room for anything else in the house. So there she was. Orders already down to a trickle, and now every rupee spoken for. She started running the math a lot of small owners run at two in the morning. What a loan would cost her. The interest on top. Whether she could ever really pay it back, or whether she would just be trading one fear for a bigger one. Whether it was time to switch the oven off for good, find a job somewhere, and stop calling the bakery a business. She had, quietly, mostly decided to stop. She just had not said the words out loud yet, because saying them would make it real.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I almost did not message you. I kept thinking, why would anyone build a website for a woman selling cakes out of her kitchen.&lt;/p&gt;

&lt;p&gt;— &lt;em&gt;Meera&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we actually changed
&lt;/h2&gt;

&lt;p&gt;When she finally wrote to us, she apologised twice in the first three lines. She was certain she could not afford it. I think part of her expected me to be polite and then quietly disappear, the way the last person had. So we did not talk about websites at first. We talked about the bakery. What she made, who bought it, what a good week used to look like. She sent me a long voice note that day, talking fast, listing everything she baked, apologising again for taking up my time. I still think about that voice note. Then we got to work, and none of it was fancy. That is honestly the point. We built the things a small business actually needs to take an order, and we left out everything it does not. This is the whole of what &lt;a href="https://dev.to/web-development-agency/"&gt;our small business web work&lt;/a&gt; is really about.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A fast, mobile-first website.&lt;/strong&gt; It opens in a second or two on a cheap phone with a weak signal, because that is exactly what her customers are holding when a craving hits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp ordering.&lt;/strong&gt; One tap from the site drops the customer into a chat with her, since that is where she was already comfortable talking to people and writing down orders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UPI and card payments.&lt;/strong&gt; So an order gets paid for right then, on the spot, instead of an "I will send the money later" that somehow never arrives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A proper Google Business Profile.&lt;/strong&gt; Real photos of her actual cakes, the correct phone number, her hours, her spot pinned on the map, so she finally showed up when someone nearby went looking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local SEO.&lt;/strong&gt; The slow, unglamorous work of making sure a hungry person two kilometres away lands on her instead of scrolling straight past to a big chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The first order from a stranger
&lt;/h2&gt;

&lt;p&gt;The first online order came in on a Tuesday afternoon. I remember it was a Tuesday because Tuesdays are slow for everybody. It was not a relative. It was not an old classmate doing her a favour. It was a stranger who had searched for a cake in her area, found her on the map, scrolled through the real photos we had put up, and ordered straight through the site. Paid up front on UPI, before Meera had even opened the message. She called me a few minutes later, and she was crying. Not the sad kind of crying. She kept asking how a person she had never met, who did not know her name or her family or her street, had found her. I did not really have a clever answer for her. That is just what a website does when it is built right. It does not perform miracles. It quietly removes every reason a ready customer had to give up and go somewhere else. The cake was there. The customer was there. For years the only thing standing between them was a slow page and a wrong phone number.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A stranger found me. I have been baking for six years and a stranger found me and paid before I even replied.&lt;/p&gt;

&lt;p&gt;— &lt;em&gt;Meera&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why we build the way we do
&lt;/h2&gt;

&lt;p&gt;Within a few weeks the oven was running full days again. She pulled in a neighbour to help with deliveries because she could not keep up with both the baking and the running around. I am not going to hand you a percentage or a revenue figure. I did not audit her books, and I am not going to invent a clean number just to make this story sound tidy. Plenty of people in my line of work would. What I can tell you honestly is this. The oven stayed warm. The loan she was terrified of never had to be taken. The lights stayed on. For a business like Meera's, that is not a small win. That is the whole thing. This is why I get a little impatient when people call a website a line item, something you buy once and forget. A website is not pixels on a screen. For the right business, built the right way, it is the difference between an oven that runs and one that goes cold and stays cold. That is the reason we build the way we do. Fast, so it opens on the oldest, cheapest phone in the market. Easy to find, so someone nearby actually lands on it. Dead simple to order from, so nothing sits between a ready customer and the thing they came for. We do the same for a tailor, a clinic, a tiffin service, a repair shop. You can see the plain version of how we &lt;a href="https://dev.to/services/"&gt;work with small businesses&lt;/a&gt; whenever you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions we get from small business owners
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I really need a website if I get orders on WhatsApp and Instagram?
&lt;/h3&gt;

&lt;p&gt;WhatsApp and Instagram are wonderful for the people who already know you exist. A website is for the ones who do not. When a stranger searches for what you sell, your Instagram rarely comes up, and your WhatsApp number is not something Google can show them. A simple site is the thing that catches that person and then hands them straight into a WhatsApp chat with you. You are not throwing away what already works. You are adding a front door so new people can walk in.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does a small business website cost?
&lt;/h3&gt;

&lt;p&gt;Less than most people are afraid it will be, and we tell you the range before you commit to anything. The real answer depends on what you actually need, and for a lot of small businesses that is not much at all. We would far rather build you the right small thing than sell you a big expensive one you will never fully use. Tell us your honest budget and we will tell you honestly what it can and cannot do.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long before people find me on Google?
&lt;/h3&gt;

&lt;p&gt;Not overnight, and anyone who promises overnight is selling you something. A Google Business Profile can start putting you on the map within a few weeks, sometimes sooner. Ranking in normal search results takes longer, usually a few months, and it depends on your area and how many others are doing the same thing. We set everything up properly from the first day so that time starts working for you instead of against you. Slow and real beats fast and fake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If your business is quieter than it should be, tell us about it. No pitch, just a real conversation about what would actually help.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Tell us about your business&lt;/a&gt;&lt;/p&gt;

</description>
      <category>websiteforsmallbusinessindia</category>
      <category>smallbusinesswebsitestory</category>
      <category>whysmallbusinessesneedawebsite</category>
      <category>localseoforsmallbusinessindia</category>
    </item>
    <item>
      <title>How to Build a Subscription Billing System in India (2026): Mandates, Proration, Dunning, GST Invoices, and Build vs Buy</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Mon, 20 Jul 2026 06:01:47 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/how-to-build-a-subscription-billing-system-in-india-2026-mandates-proration-dunning-gst-j3</link>
      <guid>https://dev.to/buildbyravirai/how-to-build-a-subscription-billing-system-in-india-2026-mandates-proration-dunning-gst-j3</guid>
      <description>&lt;p&gt;A payment gateway moves rupees once. It authorizes a card or a UPI request, settles the money, and fires a success webhook. That is where most integration guides stop, and that is exactly where the real work of a subscription business begins. Recurring revenue is not one payment repeated. It is a relationship you have to manage on a schedule, forever, and no gateway sells you that layer.&lt;/p&gt;

&lt;p&gt;In India the gap is wider than the Stripe-world tutorials admit. You cannot just save a card and re-charge it whenever you like. You register a &lt;strong&gt;mandate&lt;/strong&gt; the customer's bank approves, you live inside RBI's e-mandate rules, you respect the UPI Autopay per-debit cap, and you stamp a GST-correct tax invoice on every renewal. Get any one of those wrong and the debit fails or the invoice does not match the money in your account.&lt;/p&gt;

&lt;p&gt;This guide is the full build, in the order we actually build it: what a billing system is versus a gateway, the India mandate problem, the data model, the subscription state machine, proration maths, dunning, GST-compliant recurring invoices, the self-serve portal, revenue analytics, and an honest build-vs-buy call. We write it from production, not a vendor deck. Where a number depends on the current rulebook or your own pricing, we say so in the text, and you should verify it against your own setup before you ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a subscription billing system actually is (and what a payment gateway is not)
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;payment gateway collects money once&lt;/strong&gt;. It authorizes a card, a UPI request, or a mandate, moves the rupees, and hands you a success webhook. That is the rail, and we covered how to pick it in our &lt;a href="https://dev.to/blog/accept-payments-online-india-2026-upi-cards-autopay-costs/"&gt;accept payments in India&lt;/a&gt; post, so we will not repeat it here.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;billing system manages the relationship after the money moves&lt;/strong&gt;, on repeat, forever. It is the layer no gateway sells you. Seven jobs live here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plans and pricing:&lt;/strong&gt; tiers, trials, coupons, metered usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription lifecycle:&lt;/strong&gt; activate, upgrade, pause, cancel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mandates:&lt;/strong&gt; UPI Autopay, e-mandate, card-on-file recurring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proration:&lt;/strong&gt; mid-cycle plan changes, credits, refunds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dunning:&lt;/strong&gt; retry failed charges, notify, then suspend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring GST invoices:&lt;/strong&gt; correct place of supply, HSN/SAC, IRN&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MRR and churn reporting:&lt;/strong&gt; what is actually recurring, and what is leaking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who has outgrown a raw gateway integration:&lt;/strong&gt; Indian SaaS billing monthly, OTT and streaming, edtech, gym and coworking memberships, and D2C subscription boxes. The morning one card fails and nobody retries it, you have outgrown the gateway.&lt;/p&gt;

&lt;p&gt;We write this from production, not a vendor doc. We run recurring billing inside &lt;a href="https://dev.to/products/plugev/"&gt;PlugEV&lt;/a&gt; (metered EV session billing), &lt;a href="https://dev.to/products/cloudnx/"&gt;CloudNX&lt;/a&gt; (INR hosting renewals), and &lt;a href="https://dev.to/products/crm-multivendor/"&gt;MultiVendor CRM&lt;/a&gt; (per-tenant plans).&lt;/p&gt;

&lt;p&gt;The thesis is simple: &lt;strong&gt;you will build this layer whether you design it on purpose or it grows by accident.&lt;/strong&gt; The accidental version still runs. It just leaks money quietly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The India mandate problem: UPI Autopay, e-NACH, and RBI e-mandate rules
&lt;/h2&gt;

&lt;p&gt;In India, a recurring charge is not a saved card you re-charge whenever you like. You first register a &lt;strong&gt;mandate&lt;/strong&gt;, a standing instruction the customer's bank approves, and every future debit runs against that mandate's limits and rules. Get the mandate wrong and the debit simply fails, no matter how clean your billing logic is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPI Autopay&lt;/strong&gt; is the easiest to onboard, but it carries an AFA-free threshold of &lt;strong&gt;Rs 15,000&lt;/strong&gt; per debit. A debit at or below Rs 15,000 auto-debits hands-free; a debit above Rs 15,000 is allowed, but it triggers additional factor authentication (an OTP) on every single debit, which defeats the point of hands-free billing. That single number shapes your architecture. A Rs 24,000 annual plan cannot ride UPI Autopay hands-free, because a single debit above Rs 15,000 triggers an OTP every cycle. You either route high-value plans to another mandate type or split the amount into smaller debits that stay under the threshold. An enhanced Rs 1,00,000 AFA-free limit does exist, but only for specific categories (insurance premiums, mutual funds, and credit-card bills), not general SaaS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;e-NACH / e-mandate&lt;/strong&gt; covers higher-value collections and bank-account debits, with per-mandate limits you set at registration. These are not capped at Rs 1,00,000: that figure is the enhanced AFA-free category limit, not a universal e-NACH ceiling. General e-NACH per-mandate limits can run much higher, and banks support well above Rs 1,00,000 (the exact ceiling varies by sponsor bank and gateway). &lt;strong&gt;Card e-mandates&lt;/strong&gt; are the third rail for card-first customers. Your system should pick the mandate type per plan, not force one rail on everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RBI e-mandate rules that become code.&lt;/strong&gt; Two things you must build in: a &lt;strong&gt;pre-debit notification&lt;/strong&gt; sent to the customer at least 24 hours before each charge, and &lt;strong&gt;additional factor of authentication&lt;/strong&gt; on the first registration (and on debits above the AFA threshold). You also cannot store the raw card number. You work with &lt;strong&gt;network tokens&lt;/strong&gt; only, the same tokenisation flow we cover in our accept-payments guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model the mandate as a first-class object.&lt;/strong&gt; Give it its own row and lifecycle: &lt;code&gt;created&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;paused&lt;/code&gt;, &lt;code&gt;revoked&lt;/code&gt;, &lt;code&gt;expired&lt;/code&gt;. Do not bury the mandate as a hidden field on the subscription, because a paused or revoked mandate can outlive or precede a plan change, and you need that history when a debit bounces.&lt;/p&gt;

&lt;p&gt;Every debit and every mandate event flows back through webhooks. Apply the same idempotent, callback-driven discipline we detail in our Razorpay webhooks post: one handler, keyed on event ID, safe to replay.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data model that makes or breaks it
&lt;/h2&gt;

&lt;p&gt;Get the schema wrong and every feature you bolt on later fights you. We have rebuilt billing tables for clients who baked prices into plans, and the migration is never a weekend job. Here is the shape that holds up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Split product, plan, and price into three tables.&lt;/strong&gt; A &lt;code&gt;product&lt;/code&gt; is the thing you sell (your Pro tier). A &lt;code&gt;plan&lt;/code&gt; is a billing shape (monthly, annual). A &lt;code&gt;price&lt;/code&gt; is the actual amount. One plan carries many prices: monthly INR, annual INR, USD for overseas users, a Diwali promo. Never store the amount on the plan row. The day you launch annual billing, you will thank yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model subscriptions with subscription_items.&lt;/strong&gt; A &lt;code&gt;subscription&lt;/code&gt; links a customer to a plan, but the real charges live in &lt;code&gt;subscription_items&lt;/code&gt;. That is how one customer holds a base seat, three extra seats, and an SMS add-on without you inventing a flat combined charge. Proration later reads from these items.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoices are append-only.&lt;/strong&gt; Keep &lt;code&gt;invoices&lt;/code&gt;, &lt;code&gt;invoice_line_items&lt;/code&gt;, and &lt;code&gt;credit_notes&lt;/code&gt; as separate records. Once an invoice is finalized you never edit it. To correct it you issue a &lt;strong&gt;credit note&lt;/strong&gt;. This is not a preference, it is a GST requirement: a finalized tax invoice is an immutable document, and adjustments happen through credit notes with their own numbering series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mandates and payment_attempts get their own tables.&lt;/strong&gt; An e-mandate (UPI Autopay, card, or eNACH under the RBI framework) has a lifecycle. Every debit, retry, and failure needs a row in &lt;code&gt;payment_attempts&lt;/code&gt; so dunning has a real audit trail, not a status field you overwrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Money and immutability.&lt;/strong&gt; Store amounts as &lt;strong&gt;integer paise&lt;/strong&gt;, never floating point. Never hard-delete a billing row; soft-delete or void.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tenant isolation from day one.&lt;/strong&gt; In a multi-tenant SaaS, scope every billing table by &lt;code&gt;tenant_id&lt;/code&gt;. See our &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/#billing"&gt;per-tenant billing section&lt;/a&gt; for the isolation model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The subscription lifecycle as a state machine
&lt;/h2&gt;

&lt;p&gt;Every billing bug we have cleaned up traces back to a subscription being in a state nobody named. So name them first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The six states worth modeling:&lt;/strong&gt; &lt;code&gt;trialing&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;past_due&lt;/code&gt;, &lt;code&gt;paused&lt;/code&gt;, &lt;code&gt;canceled&lt;/code&gt;, and &lt;code&gt;expired&lt;/code&gt;. Fix the legal transitions and forbid the rest. &lt;code&gt;trialing&lt;/code&gt; goes to &lt;code&gt;active&lt;/code&gt; or &lt;code&gt;canceled&lt;/code&gt;, never straight to &lt;code&gt;expired&lt;/code&gt;. &lt;code&gt;active&lt;/code&gt; slips to &lt;code&gt;past_due&lt;/code&gt; when a debit fails, then either recovers to &lt;code&gt;active&lt;/code&gt; or ends in &lt;code&gt;canceled&lt;/code&gt; after dunning gives up. &lt;code&gt;paused&lt;/code&gt; only comes from &lt;code&gt;active&lt;/code&gt; and only returns to &lt;code&gt;active&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drive transitions from events, not a cron job guessing.&lt;/strong&gt; A mandate debit succeeded, a debit failed, a cancel was requested: each is an event that moves the machine. Razorpay tells you this through webhooks (&lt;code&gt;subscription.charged&lt;/code&gt;, &lt;code&gt;subscription.halted&lt;/code&gt;), so handle them idempotently. Store the event ID, check it before you act, and a retried webhook never double-advances a subscription. See our &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks guide&lt;/a&gt; for the dedupe pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trials and coupons touch the invoice, not the mandate.&lt;/strong&gt; A 14-day trial or a first-cycle 30% off changes what the first invoice charges. The e-mandate you registered still authorizes the full recurring amount (though on UPI Autopay any debit above ₹15,000 needs an OTP each time, so it is not truly hands-free above that threshold). Discount the line item, keep the mandate intact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pause and resume is a real Indian retention lever.&lt;/strong&gt; Gym, OTT, and coworking members freeze for a month instead of canceling. A &lt;code&gt;paused&lt;/code&gt; state that stops billing but keeps the mandate alive saves the relationship. Cancel does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The renewal engine&lt;/strong&gt; is a scheduled job that finds due subscriptions, raises the GST invoice, and triggers the debit. Make it re-runnable: guard on &lt;code&gt;invoice_already_raised_for_period&lt;/code&gt; so a re-run never bills twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cancellation:&lt;/strong&gt; &lt;code&gt;cancel_at_period_end&lt;/code&gt; lets access run out the paid cycle; immediate cancel revokes now. Either way, cancel the mandate at the gateway so no orphan debit fires later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proration: the maths of mid-cycle upgrades and downgrades
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Proration in one line:&lt;/strong&gt; when a customer changes plans mid-cycle, charge them fairly for the part of the cycle they actually used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade path.&lt;/strong&gt; Credit the unused time on the old plan, charge the new plan immediately, and net the difference. The customer pays the gap now and their renewal date usually stays the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downgrade path.&lt;/strong&gt; Do not refund cash. Bank a &lt;strong&gt;credit&lt;/strong&gt; and apply it against the next invoice. Refunds trigger reconciliation, RBI mandate mess, and support tickets you do not want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compute it without bugs.&lt;/strong&gt; Derive a &lt;strong&gt;per-second or per-day rate&lt;/strong&gt; from the full cycle, not a rounded monthly figure. Work in &lt;strong&gt;integer paise&lt;/strong&gt; end to end and round once, at the very end. Example: a customer on the ₹999 plan upgrades to ₹2,499 on day 20 of a 30-day cycle. Unused credit is 999 × (10/30) = ₹333. New plan charge for the remaining 10 days is 2,499 × (10/30) = ₹833. They pay ₹500 today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proration is invoicing, not just arithmetic.&lt;/strong&gt; A downgrade credit becomes a &lt;strong&gt;GST credit note&lt;/strong&gt; against the original invoice. An upgrade charge is a &lt;strong&gt;new taxable line&lt;/strong&gt; at 18% GST. Get the document type wrong and your GSTR filing breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The harder cousin: metered billing.&lt;/strong&gt; On &lt;strong&gt;PlugEV&lt;/strong&gt; the charge depends on kWh consumed, not a flat plan, so we rate usage events into paise continuously and invoice the total at cycle close. Same integer discipline, more moving parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We test the proration engine like a payments path.&lt;/strong&gt; An off-by-one-day error looks tiny in a unit test and bleeds real money at scale, so we run fixed-clock cases across every upgrade, downgrade, and trial-conversion boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dunning: how to stop involuntary churn eating your MRR
&lt;/h2&gt;

&lt;p&gt;Not all churn is a customer walking away. &lt;strong&gt;Voluntary churn&lt;/strong&gt; is someone who cancels on purpose. &lt;strong&gt;Involuntary churn&lt;/strong&gt; is someone who wanted to stay but whose auto-debit failed. Industry estimates put involuntary churn (failed payments and expired mandates) at roughly 20 to 40 percent of total subscription churn, and that slice is recoverable if you build for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The retry ladder.&lt;/strong&gt; Don't hammer a failed mandate the same day. Space retries out, for example day 1, 3, 5, and 7, and tune the schedule to the actual failure reason. In India the common ones are insufficient balance, a paused mandate, and bank downtime. Insufficient balance clears near salary dates, so a day-5 or day-7 retry often lands when day-1 won't. Razorpay Subscriptions runs a retry schedule but gives you limited control over its timing and messaging; a rules-based cadence like this, with your own retry days and pre-debit copy, needs a dedicated tool like Chargebee or a custom dunning layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-debit notifications feed dunning.&lt;/strong&gt; The RBI-mandated pre-debit heads-up (24 hours before the debit) is also your best churn-reducer, because customers top up their account when they see it coming. Treat it as the first rung of dunning, not a compliance checkbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-channel, but respect the rules.&lt;/strong&gt; Send reminders in-app, over email, and via WhatsApp or SMS. SMS and promotional WhatsApp go through DLT-registered templates, so wire your dunning copy into the same DLT-compliant notification pipeline you already use for transactional messages, or they won't deliver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grace period and the cancel path.&lt;/strong&gt; Decide how long a subscription stays &lt;code&gt;past_due&lt;/code&gt; before it expires. A 7 to 14 day grace window is common. Keep the customer's access live during grace so a recovered payment restores service without a break, then downgrade or expire once retries are exhausted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure it.&lt;/strong&gt; Track &lt;strong&gt;recovery rate&lt;/strong&gt; (failed payments recovered divided by total failed) per cohort and per retry day. Without that number, dunning is hope. With it, you can move retry days and reminder channels and see the MRR you saved.&lt;/p&gt;

&lt;h2&gt;
  
  
  GST-compliant recurring invoices
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Invoice on the settled amount, not the attempt.&lt;/strong&gt; When Razorpay or a NACH mandate fires a renewal, wait for the webhook that confirms the debit cleared, then generate the invoice against that captured payment. If you invoice on the gross plan price at the attempt stage, a failed or partial debit leaves you with an invoice that never matches the money in your account. Tie every invoice to a real &lt;code&gt;payment_id&lt;/code&gt;, not a scheduled charge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the number series audit-clean.&lt;/strong&gt; Invoice numbers must run sequentially per financial year and per GSTIN. Reset the counter on 1 April, prefix by GSTIN if you bill from more than one state, and never reuse or skip. For downgrades, mid-cycle proration credits, and refunds, issue a &lt;strong&gt;GST credit note&lt;/strong&gt; linked to the original invoice rather than editing it. That keeps the sequence intact when an auditor pulls it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get place of supply right.&lt;/strong&gt; For SaaS the customer's registered state decides the tax split. Same state as your GSTIN gets &lt;strong&gt;CGST + SGST&lt;/strong&gt;, a different state gets &lt;strong&gt;IGST&lt;/strong&gt;, both at 18%. Your billing engine has to store and read the customer state before it stamps the line, so capture GSTIN and state at signup. Use &lt;strong&gt;SAC 9983 / 998314&lt;/strong&gt; for software services on every recurring line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know the boundary.&lt;/strong&gt; This is the billing engine cutting the invoice. IRP submission and IRN generation are a separate step once you cross the e-invoicing turnover threshold (see our GST e-invoicing post), and booking these into the ledger belongs in Zoho Books or Tally (see those posts).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send it automatically.&lt;/strong&gt; On every successful renewal, auto-email the PDF invoice and expose the full history in the customer self-serve portal so nobody raises a ticket asking for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The customer self-serve billing portal
&lt;/h2&gt;

&lt;p&gt;The portal exists so your inbox stops filling with billing tickets. A customer should log in and, without emailing anyone, &lt;strong&gt;view past invoices&lt;/strong&gt;, &lt;strong&gt;download the GST invoice&lt;/strong&gt; (with your GSTIN, HSN/SAC, and tax split), see the &lt;strong&gt;next debit date and amount&lt;/strong&gt;, and &lt;strong&gt;upgrade, downgrade, pause, or cancel&lt;/strong&gt;. If they can't do these six things themselves, support does it for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mandate re-registration is the one to get right.&lt;/strong&gt; When a card expires or a customer revokes a mandate at their bank, the debit silently fails and that is churn nobody noticed. Detect the dead mandate, email/WhatsApp the customer, and drop them into a one-click re-registration flow before the next cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make cancel a save flow, not a button.&lt;/strong&gt; Before the final confirm, offer &lt;strong&gt;pause&lt;/strong&gt; (reuse your lifecycle pause state) or a &lt;strong&gt;downgrade&lt;/strong&gt; to a cheaper tier. Many "cancels" are really "too expensive this month."&lt;/p&gt;

&lt;p&gt;Let them &lt;strong&gt;change payment method&lt;/strong&gt; and &lt;strong&gt;switch rail&lt;/strong&gt;: a customer moving to an annual plan whose per-debit amount clears the UPI Autopay AFA-free threshold (INR 15,000) would face an OTP on every debit, so it makes sense to shift them from UPI Autopay to &lt;strong&gt;e-NACH&lt;/strong&gt;, and you should surface that automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolation is non-negotiable.&lt;/strong&gt; Every route is authenticated and &lt;strong&gt;tenant-scoped&lt;/strong&gt;; customer A can never load customer B's invoice by guessing an ID.&lt;/p&gt;

&lt;p&gt;Each self-serve action is a support ticket that never gets raised. That is how it pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revenue analytics: MRR, churn, and revenue recognition
&lt;/h2&gt;

&lt;p&gt;The number your investors ask for first is &lt;strong&gt;MRR&lt;/strong&gt; (monthly recurring revenue), and &lt;strong&gt;ARR&lt;/strong&gt; is just MRR times 12. But the headline figure hides the story. Break it into &lt;strong&gt;new MRR&lt;/strong&gt; (fresh signups), &lt;strong&gt;expansion MRR&lt;/strong&gt; (upgrades and add-ons), &lt;strong&gt;contraction MRR&lt;/strong&gt; (downgrades), and &lt;strong&gt;churned MRR&lt;/strong&gt; (cancellations). That breakdown is what tells you whether growth is healthy or you are just outrunning a leak.&lt;/p&gt;

&lt;p&gt;Watch &lt;strong&gt;gross vs net revenue churn&lt;/strong&gt;. Gross churn counts only what you lost. Net churn subtracts expansion, so a few big upgrades can mask a base that is quietly bleeding. If net churn looks fine but gross churn is climbing, your base is leaky and the wins are covering for it.&lt;/p&gt;

&lt;p&gt;Here is the trap your payment dashboard sets: &lt;strong&gt;cash collected is not revenue recognized&lt;/strong&gt;. An annual plan paid upfront in January is INR 12,000 in the bank but only INR 1,000 of recognized revenue that month. The rest is &lt;strong&gt;deferred revenue&lt;/strong&gt;, released month by month.&lt;/p&gt;

&lt;p&gt;Razorpay cannot give you any of this. The gateway sees transactions, not subscription state, so MRR, churn, and deferred schedules only exist if &lt;strong&gt;your billing layer computes and stores them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then feed the accountant. Reconcile recognized revenue to the books by posting into &lt;strong&gt;Zoho Books&lt;/strong&gt; or &lt;strong&gt;Tally&lt;/strong&gt; (invoices, deferred entries, GST), so finance and product agree on one number.&lt;/p&gt;

&lt;p&gt;Finally, run &lt;strong&gt;cohort retention&lt;/strong&gt;: group customers by signup month and track how many still pay at month 3, 6, 12. That single report tells you if the product is actually sticky.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build vs buy: custom vs Razorpay Subscriptions vs Chargebee vs Zoho
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Razorpay Subscriptions&lt;/strong&gt; sits on the rail you already use. It handles the hard part: creating e-mandates (UPI AutoPay, card, eNACH), charging on schedule, and running the RBI-mandated pre-debit notification flow. Plans and basic trials work out of the box. The ceiling shows up fast. Proration is thin, so mid-cycle upgrades and metered usage need your own math. Dunning runs a retry schedule, but you get limited control over its timing and messaging, not the rules-based cadence you would design yourself. And GST invoices are still yours to shape: Razorpay gives the charge event, you build the compliant tax invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chargebee and Zoho Subscriptions&lt;/strong&gt; are mature. You get full lifecycle (pause, resume, downgrade), a hosted customer portal, coupons, and MRR/churn analytics without building any of it. The tradeoff is the cost model and where your data lives. Chargebee moves to percentage-of-revenue or per-tier billing that grows as you grow; Zoho is per-tier by customer/user count. Either way your subscription data now lives outside your app, and every report or automation crosses an API boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom genuinely wins&lt;/strong&gt; when proration is complex or metered, when your pricing is product-specific, when billing is tightly coupled to your app, and when a revenue-share bill would hurt at scale. Our &lt;strong&gt;PlugEV&lt;/strong&gt; metered EV-charging billing is exactly the case bought tools handle badly: per-kWh usage, session-level rating, no clean plan fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hybrid most teams should pick:&lt;/strong&gt; keep Razorpay as the rail and mandate engine, and build the domain layer yourself (lifecycle, proration, dunning rules, GST invoicing, analytics). You own the data without rebuilding payments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest INR ranges:&lt;/strong&gt; a full custom billing layer (plans, proration, dunning, recurring GST invoicing, a self-serve portal, and MRR/churn analytics) is roughly Rs 6,00,000 to Rs 18,00,000 to build, then low maintenance. A more focused build, scoped to your specific plan and mandate mix rather than the whole platform, is roughly Rs 3,50,000 to Rs 9,00,000 and usually a 4 to 10 week build. A bought tool typically runs about 0.5 to 0.75 percent of billed revenue, or roughly Rs 15,000 to Rs 60,000 and up per month depending on tier and volume (vendor pricing changes, so check the current pricing page). Compare over three years, not month one. At scale, revenue-share overtakes a one-time build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan complexity: flat tiers or metered/usage-based?&lt;/li&gt;
&lt;li&gt;Mandate mix: UPI AutoPay, cards, eNACH?&lt;/li&gt;
&lt;li&gt;GST needs: simple or multi-state, e-invoicing?&lt;/li&gt;
&lt;li&gt;Analytics depth: dashboard cards or cohort-level?&lt;/li&gt;
&lt;li&gt;Is billing core to your product, or a side feature?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we build subscription billing, and when we tell you not to
&lt;/h2&gt;

&lt;p&gt;Our default shape is a &lt;strong&gt;Node.js backend&lt;/strong&gt; where the lifecycle (plan changes, proration, dunning retries, reconciliation) is covered by tests, because these are money paths and deserve to be treated like it. That is the core of our &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js development&lt;/a&gt; work.&lt;/p&gt;

&lt;p&gt;We ship this from lived work, not theory. The same engine runs metered per-session billing, INR renewals with GST invoices, and per-tenant plans across the products we operate, so the edge cases we describe here are ones we have actually hit and fixed.&lt;/p&gt;

&lt;p&gt;The honest part: if you run a single flat plan and a few hundred subscribers, &lt;strong&gt;Razorpay Subscriptions&lt;/strong&gt; is enough, and we will say so. Custom earns its cost once metering, mid-cycle proration, or data ownership make it pay: a focused build scoped to your specific plan and mandate mix (not the whole platform) is usually a 4 to 10 week build at roughly INR 3.5 to 9 lakh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I build a custom subscription billing system or just use Razorpay Subscriptions, Chargebee, or Zoho Subscriptions?
&lt;/h3&gt;

&lt;p&gt;If you run a single flat plan with a few hundred subscribers, Razorpay Subscriptions is genuinely enough and we would tell you not to build. Custom starts to earn its cost when you have metered or usage-based pricing, complex mid-cycle proration, or a reason to keep your subscription data inside your own app instead of a vendor's. The middle path most teams should take is hybrid: keep Razorpay as the rail and mandate engine, and build the lifecycle, proration, dunning, and GST invoicing yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the UPI Autopay limit for subscriptions in India, and how do I bill high-value annual plans above it?
&lt;/h3&gt;

&lt;p&gt;UPI Autopay carries an AFA-free threshold of Rs 15,000 per debit: a charge at or below that auto-debits hands-free, while a charge above it is allowed but triggers additional factor authentication (an OTP) on every debit, which defeats hands-free billing. For a high-value annual plan you have two clean options: route it to an e-NACH or card e-mandate, whose per-mandate limits are set at registration and can run well above Rs 1,00,000 (that Rs 1,00,000 figure is the enhanced AFA-free category limit, not a universal e-NACH ceiling), or split the amount into smaller scheduled debits that each stay under the threshold. Your system should pick the mandate type per plan rather than forcing every customer onto one rail.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do recurring GST invoices work when a customer upgrades or downgrades mid-cycle?
&lt;/h3&gt;

&lt;p&gt;An upgrade adds a new taxable line for the prorated difference, invoiced at 18% GST like any normal charge. A downgrade does not edit the original invoice, because a finalized tax invoice is immutable; instead you issue a GST credit note linked to it, banking the credit against the next cycle rather than refunding cash. In both cases the place of supply (the customer's registered state) decides whether the split is CGST plus SGST or IGST, so capture GSTIN and state at signup.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it cost to build a custom subscription billing system in India in 2026?
&lt;/h3&gt;

&lt;p&gt;A full custom billing layer, meaning plans, proration, dunning, recurring GST invoicing, a self-serve portal, and MRR/churn analytics, runs roughly Rs 6,00,000 to Rs 18,00,000 to build, then stays low on maintenance. A more focused build, scoped to your specific plan structure and mandate mix rather than the whole platform, is usually a 4 to 10 week engagement at around Rs 3,50,000 to Rs 9,00,000. Compare either against a bought tool, which typically runs about 0.5 to 0.75 percent of billed revenue, or roughly Rs 15,000 to Rs 60,000 and up per month depending on tier and volume (vendor pricing changes, so check the current pricing page), and do the sum over three years, because revenue-share pricing overtakes a one-time build as you scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I reduce involuntary churn from failed auto-debits and expired mandates?
&lt;/h3&gt;

&lt;p&gt;Build a retry ladder that spaces attempts out (for example day 1, 3, 5, and 7) and tunes to the failure reason, since insufficient-balance failures often clear near salary dates. Treat the RBI pre-debit notification as your first dunning rung, send reminders across in-app, email, and DLT-registered WhatsApp or SMS, and keep access live through a 7 to 14 day grace window. Detect dead mandates and drop customers into a one-click re-registration flow, then track recovery rate per retry day so you can see the MRR you save.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I keep Razorpay for payments but build the subscription and billing logic myself?
&lt;/h3&gt;

&lt;p&gt;Yes, and this is the setup we recommend for most growing teams. Razorpay stays as the rail and mandate engine, handling e-mandate creation, scheduled charges, and the pre-debit notification flow, while you build the domain layer on top: subscription lifecycle, proration, dunning rules, GST invoicing, and revenue analytics. You own your subscription data and reporting without taking on the burden of rebuilding payments or PCI-scope card handling.&lt;/p&gt;

&lt;p&gt;The honest summary: this layer gets built either way, on purpose or by accident, and the accidental version is the one that leaks money quietly. If you want a straight build-vs-buy call for your specific plan structure, mandate mix, and GST footprint, &lt;a href="https://dev.to/contact/"&gt;talk to us&lt;/a&gt; and we will give you the real answer, even when it is "just use Razorpay Subscriptions."&lt;/p&gt;

</description>
      <category>subscriptionbillingsystemindia</category>
      <category>recurringbillingsoftwareindia</category>
      <category>upiautopayrecurringpaymentsfor</category>
      <category>emandateenachrecurringbilling</category>
    </item>
    <item>
      <title>DLT-Compliant Notification Service Architecture in India (2026): SMS, WhatsApp, and Email Without Silent OTP Failures</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Fri, 17 Jul 2026 15:11:51 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/dlt-compliant-notification-service-architecture-in-india-2026-sms-whatsapp-and-email-without-105c</link>
      <guid>https://dev.to/buildbyravirai/dlt-compliant-notification-service-architecture-in-india-2026-sms-whatsapp-and-email-without-105c</guid>
      <description>&lt;p&gt;Every Indian SaaS team eventually hits the same wall. Your notification code works fine in staging, ships to production, and then one Tuesday a customer calls support because the OTP never arrived. You check the gateway dashboard. Delivered. You check your application logs. 200 OK. Nothing in your stack disagrees with anything else in your stack, and yet the message is not on the handset.&lt;/p&gt;

&lt;p&gt;This post is about the layer that sits between your domain events and your SMS gateway, the one nobody sells you and everybody ends up building by accident. It covers the template registry that makes DLT drift a build failure instead of a 2am incident, the idempotency discipline that stops failover from double-sending OTPs, the channel ladder that crosses from Meta's approval regime into TRAI's without anybody noticing, and the reconciliation job that finally answers whether the message landed.&lt;/p&gt;

&lt;p&gt;We are writing this from production, not from a vendor doc. We run these flows on &lt;strong&gt;MultiVendor CRM&lt;/strong&gt; (lead and order alerts) and &lt;strong&gt;PlugEV&lt;/strong&gt; (session start/stop, payment receipts). The architecture below is the one we maintain, including the parts where we tell you not to build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivered in Our Logs, Never on the Handset
&lt;/h2&gt;

&lt;p&gt;The project always starts with the same ticket. A customer swears the OTP never came. Your gateway dashboard shows &lt;strong&gt;DELIVERED&lt;/strong&gt;. Your application logs show &lt;strong&gt;200 OK&lt;/strong&gt; from the send call. Three systems agree, and all three are wrong. Nobody is lying, they are each reporting on a different hop, and none of them can see the handset.&lt;/p&gt;

&lt;p&gt;In our experience, the failure is almost always one of three things. &lt;strong&gt;Template drift&lt;/strong&gt;: someone edited the marketing copy in the DLT portal, the variable count changed, and the operator now silently rejects a message your gateway still acks. &lt;strong&gt;Header category mismatch&lt;/strong&gt;: a transactional OTP going out on a promotional header, then getting swallowed by DND filters for exactly the users who complain loudest. &lt;strong&gt;A fallback rung that never fired&lt;/strong&gt;: your code has a WhatsApp retry, but the SMS provider returned a soft success, so the retry never triggered. Each of these has a design answer, not a checklist item.&lt;/p&gt;

&lt;p&gt;There is a fourth failure mode, and this one is written into the regulation itself. Clause 8(e) of the &lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;18 November 2025 TRAI Direction&lt;/a&gt; mandates a "logger mode" for the first sixty days from the start of variable-tag scrubbing: during that window, messages that fail variable-tag validation (the pre-tagging checks we cover below) are still delivered, after the operator generates a fault message, and only after the sixty days are failing messages rejected outright. Read that again. The regulator required a period where broken templates keep landing on handsets while quietly generating faults you never see, and clause 8(f) puts the duty to identify and notify the affected Principal Entity on the Access Provider, not on you. That is the green-dashboard, broken-system pathology this whole post is about, except mandated by law, which means your gateway may have been sitting on fault messages about your templates that you were never shown. When scrubbing crosses its sixty-day mark, those same messages stop being delivered. If you did not fix them during logger mode, they fail hard on the day the grace ends.&lt;/p&gt;

&lt;p&gt;Scope, plainly. This assumes you already have an entity ID, a registered header, and a gateway account. Registration is your vendor's blog post, not ours.&lt;/p&gt;

&lt;p&gt;The thesis: &lt;strong&gt;your SMS vendor cannot sell you the queue, the template registry, the idempotency layer, or the reconciliation job.&lt;/strong&gt; You own that layer whether you designed it or it grew by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  DLT Is a Schema Constraint, Not Paperwork
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Treat DLT like a database migration, not a GST filing.&lt;/strong&gt; A filing is something you do once and forget. A migration is something your code knows about, your CI enforces, and your deploy fails on when it drifts. DLT decides the shape of your message payload: which header sends it, which template ID it must match, how many variables it carries, and where they sit in the string. That is schema. It belongs in your codebase, not in a spreadsheet somebody's ops lead maintains.&lt;/p&gt;

&lt;p&gt;Six layers own six different things, and most broken systems have collapsed all six into one controller. The &lt;strong&gt;domain event&lt;/strong&gt; says &lt;code&gt;OrderShipped&lt;/code&gt; and nothing more. The &lt;strong&gt;notification intent&lt;/strong&gt; decides this event deserves a message and who receives it. The &lt;strong&gt;channel policy&lt;/strong&gt; picks SMS, WhatsApp, or email based on regime (transactional OTP versus service-explicit promo) and consent state. &lt;strong&gt;Template resolution&lt;/strong&gt; maps intent plus channel plus locale to a registered DLT template and its variable slots. The &lt;strong&gt;provider adapter&lt;/strong&gt; speaks one gateway's HTTP dialect. &lt;strong&gt;DLR reconciliation&lt;/strong&gt; closes the loop and tells you it actually landed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your domain code should never know a Template ID exists.&lt;/strong&gt; The moment &lt;code&gt;checkout_controller.py&lt;/code&gt; contains &lt;code&gt;"1707161234567890123"&lt;/code&gt;, you have lost. We see this in almost every audit: bare string literals pasted across controllers, no registry, no single call site. When an operator rejects a template, nobody can grep their way to every usage, so the fix ships partial and OTPs keep failing for the one path someone missed.&lt;/p&gt;

&lt;p&gt;The PE to telemarketer binding chain is deploy-time configuration, not application logic, and it helps to know why. On every message the operator's scrubber validates five things: your PE ID, the header, the content template ID, the PE-to-telemarketer binding chain, and the recipient's DND status. Four of those five are static config for a given deploy, which is exactly why they belong in typed config that fails loudly on boot, not env-var soup that fails silently at 2am. Entity ID, header, and template IDs go in that config. And if you are building multi-tenant, each tenant may carry its own entity ID and header, so make the registry tenant-aware on day one rather than retrofitting it under load.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Template Registry: Your Approved Body, in Version Control, Checked by CI
&lt;/h2&gt;

&lt;p&gt;Check the operator-approved body, character for character, into version control next to the code that renders it. This is the single highest-leverage move in this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The shape to copy is a registry object keyed by domain name, not by template ID.&lt;/strong&gt; &lt;code&gt;otp.login&lt;/code&gt;, &lt;code&gt;order.shipped&lt;/code&gt;, &lt;code&gt;payment.failed&lt;/code&gt;. Each entry holds the &lt;code&gt;templateId&lt;/code&gt; the operator issued, the registered &lt;code&gt;body&lt;/code&gt; string exactly as approved, the declared variables with their types, the category (transactional, service-implicit, promotional), and the registered header. One file. Greppable. When a backend dev asks "which sender ID does the OTP go out on," the answer is a single search, not a Slack thread with your gateway account manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CI test that earns its keep renders each template with fixture values, then asserts the static text matches the approved body exactly.&lt;/strong&gt; Not fuzzy, not trimmed, not normalized. A stray double space between "OTP" and "is" fails the build. A PM changing &lt;code&gt;Rs.&lt;/code&gt; to &lt;code&gt;INR&lt;/code&gt; in a "harmless copy tweak" fails the build. A smart quote pasted out of a Google Doc, the one that renders identically in every editor you own, fails the build. All of these otherwise fail at the operator's scrubber instead, at 2am, silently, on your live login flow.&lt;/p&gt;

&lt;p&gt;This is why the registry beats the checklist posts already ranking for this. "Fix your template mismatch" is reactive advice you apply after production breaks. A CI assertion makes drift structurally impossible to merge. Same problem, one level up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Registry-level validation also catches the variable ceilings at build time.&lt;/strong&gt; Under the pre-tagging regime this post is about, limits are per tag, not a single global 30-character ceiling. The Direction's Annexure-I caps &lt;code&gt;#alphanumeric#&lt;/code&gt; at 40 characters in scrubbing ("Not more than 40 Characters will be allowed in scrubbing"), while &lt;code&gt;#cbn#&lt;/code&gt; runs 3 to 14 and &lt;code&gt;#url#&lt;/code&gt; allows 120 (&lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;TRAI Direction, 18 Nov 2025, Annexure-I&lt;/a&gt;). That is exactly why the type belongs in the registry entry: one ceiling per slot type, asserted in CI. And length is not even the sharp edge. &lt;code&gt;#alphanumeric#&lt;/code&gt; is specified as letters and numbers only, so a customer named "Venkataraman Subramanian", well under any length limit, still fails type validation on the space in the middle, and a hyphenated order ID fails on the hyphen. Those become failing tests instead of an incident and a refund.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The registry also has to assert two rules that predate pre-tagging and that most checklist posts miss entirely.&lt;/strong&gt; First, variable count: the number of variable portions in a content template is capped at two, a third allowed only for reasons recorded as an exigency, and more than that only on written requisition with justification from the Principal Entity. Second, adjacency: variables must be non-contiguous, never placed back to back and never separated only by a space, comma, or other special character. Both come from TRAI's 16 February 2023 Direction, recounted in the &lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;18 November 2025 Direction&lt;/a&gt;. Put a variable-count assertion and an adjacency check in the same CI test that matches the body, and a template that quietly grows a third variable in a copy edit fails the build instead of the scrubber.&lt;/p&gt;

&lt;p&gt;Version the registry deliberately. When an operator approves a new body, it lands as a PR with a reviewable diff, which means you can revert a bad template as fast as you shipped it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typed Variables: TRAI's 2026 Pre-Tagging Is a Type System Now
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pre-tagging is not new, and getting its history right matters for how you plan around it.&lt;/strong&gt; TRAI directed variable pre-tagging back in its 12 May 2023 Direction, and the principle is already written into item 4(3) of Schedule-I of the TCCCPR 2018: each variable in a message template should be pre-tagged for the purpose it is proposed to be used. The reason the &lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;18 November 2025 Direction&lt;/a&gt; exists at all is that, in TRAI's own words at para 7(a), the earlier directions "have not been implemented till date" (see also &lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/PR_No.133_of_2025.PDF" rel="noopener noreferrer"&gt;TRAI Press Release 133/2025&lt;/a&gt;). The 2025 Direction universalises tagging to every variable field and attaches a hard rejection date. So the industry got roughly two and a half years of grace, and what expired in 2026 was the grace, not the rule. Most coverage treated this as a registration chore. It is not. It is a schema change to your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dates are widely misreported, so pin them to the Direction.&lt;/strong&gt; Clause 8(c) requires all new content templates registered after ten days from the date of issue, that is from 18 November 2025, to be pre-tagged. Clause 8(d) gives the remaining, older templates sixty days to comply, and the clock starts from when your operator begins variable-tag scrubbing, not from any template's registration date and not from a fixed calendar deadline. Gateways reported scrubbing commencing around mid-January 2026, which put the practical migration deadline near mid-March 2026. The "14 January 2026" date circulating in vendor posts is at best the day operators started scrubbing, not a TRAI deadline, so confirm your operator's actual scrubbing start date with your gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The six tags are types, and your template abstraction has to carry them.&lt;/strong&gt; #numeric#, #alphanumeric#, #url#, #urlott#, #cbn#, #email#. One nuance worth reading the Direction for rather than a vendor summary: Annexure-I writes the first as "#number# / #numeric#", so check which spelling your operator's portal actually accepts (&lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;TRAI Direction, 18 Nov 2025, Annexure-I&lt;/a&gt;). Before pre-tagging, a variable was a hole you dropped a string into and the operator's scrubber sorted it out. Now every slot has a declared type that lives on the operator's side, and your code has no idea about it unless you put it there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declare the type in your template registry and validate at the boundary.&lt;/strong&gt; If your registry entry for &lt;code&gt;ORDER_CONFIRM&lt;/code&gt; says slot 2 is &lt;code&gt;#numeric#&lt;/code&gt;, that fact belongs in the entry, enforced with Zod or whatever you already use. Then a bad send throws inside your process, with a stack trace, at the call site. The alternative is finding out from a delivery report the next morning, or worse, from a customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is the bug this catches.&lt;/strong&gt; You register a slot as #numeric# because your fixtures are &lt;code&gt;4471&lt;/code&gt;. Production order IDs are &lt;code&gt;ORD-4471&lt;/code&gt;. Staging is green, production OTPs and confirmations quietly die at the scrubber, and nothing in your logs says "type rejection" because the rejection happened at someone else's shop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;URLs are the sharp edge.&lt;/strong&gt; #url# and #urlott# are not interchangeable, shortened links and UTM parameters interact badly with whitelisted-domain rules, and the failure is invisible from your side. When marketing swaps the shortener next quarter, your template breaks and nobody connects the two events.&lt;/p&gt;

&lt;p&gt;Pre-tagging is a code-layer change. That is exactly why the gateways who sent migration notices could tell you it was coming, but not what to refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Header and Category Routing: The Choice That Silently Kills DND Delivery
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure mode here looks like nothing at all.&lt;/strong&gt; Register a header under the wrong category and your OTP is legally a promotional message. Every subscriber with DND active never sees it. The gateway still returns a message ID, your service still logs &lt;code&gt;sent&lt;/code&gt;, your dashboards stay green, and your error rate is zero. Nothing in your stack ever says blocked. You find out from support tickets about users who cannot log in, weeks late. Hundreds of millions of Indian numbers carry a DND preference (NCPR registrations past 230 million), so even at a conservative share this is a large, silent slice of your delivery, not an edge case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category belongs in the registry as a routing decision, never in a vendor dashboard.&lt;/strong&gt; Every template row declares its category and resolves to exactly one header. The router reads that at lookup time and refuses to put an OTP on a promotional header. A toggle somebody flipped in a gateway console six months ago is not a system, it is a rumour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Untangle the three names before mapping anything.&lt;/strong&gt; Service Implicit, Service Explicit, and Transactional are not what most backend teams assume. In developer speech "transactional" just means "not marketing". Under DLT rules, Transactional is a narrow category restricted to banks sending OTPs and account alerts, open to national, scheduled, private, government, and MNC banks and nobody else. Most order updates and shipment alerts, and every OTP from a non-bank product, are Service Implicit, not Transactional. We have seen teams register everything as Transactional and then wonder why traffic gets filtered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consent is data, not a checkbox.&lt;/strong&gt; Service Explicit means producing the record on demand, so store principal, timestamp, source (which form, which screen, which IVR flow), scope, and revocation state. Those records and the message bodies are personal data under a second regime, which we cover in our &lt;a href="https://dev.to/blog/dpdp-act-compliance-india-2026-website-saas-guide/"&gt;DPDP compliance guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our build rule:&lt;/strong&gt; one header per category, wired into the registry, resolved at lookup. If a template has no header for its category, the build fails instead of production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Channel Ladder, and the Regime Boundary Nobody Designs For
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When your OTP falls from WhatsApp to SMS, it does not just change transport, it changes jurisdiction.&lt;/strong&gt; The WhatsApp rung lives under Meta's utility template rules, approved by Meta. The rung directly below it lives under TRAI's DLT regime, approved through your operator's DLT portal via your gateway. Two templates, two approval bodies, two sets of failure modes, aimed at the same user in the same second. Most notification code we inherit treats that boundary as a config flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The design consequence: your fallback rung needs its own registered DLT template and header, approved before you ever need it.&lt;/strong&gt; Teams learn this mid-incident. WhatsApp goes flaky at 9pm, someone reroutes OTPs to SMS, and the gateway rejects every send because there is no approved template ID and no registered Service Implicit header for that content. A new content template is typically one to three working days to approve, and a header you do not already have adds one to two more, so the full path from nothing to a live fallback runs closer to three to seven working days. Your incident does not wait that long.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ladder we actually build:&lt;/strong&gt; WhatsApp utility template, then SMS on a Service Implicit header, then email. Transactional if you are a bank, Service Implicit for everyone else, which is most readers, and neither MultiVendor CRM nor PlugEV is a bank. Each rung owns its own entry in the template registry, its own timeout, and its own delivery contract. The registry is what makes the regime boundary explicit in code instead of leaving it in one engineer's head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not ladder on optimism.&lt;/strong&gt; WhatsApp "sent" is not "read", and silence is not failure. Define the promotion signal explicitly: a failed status callback, or no delivery confirmation inside N seconds. Then cap total attempts, or a flapping provider fans out four messages to one user, who now holds four OTPs and trusts none of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost asymmetry is real and it changes the design.&lt;/strong&gt; Since 1 July 2025, WhatsApp bills per delivered template message, not per conversation, and the old "utility conversation" billing unit is gone (&lt;a href="https://developers.facebook.com/docs/whatsapp/pricing/updates-to-pricing/" rel="noopener noreferrer"&gt;Meta pricing updates&lt;/a&gt;). Utility templates sent inside an open customer-service window are free, while authentication templates are billed even inside that window, which is the opposite of the asymmetry most teams assume. Your OTP rung is precisely the category that never rides the free window. We will not quote rates we cannot defend, but the model is a fact: authentication is metered, so model the ladder at your real volume before you tune timeouts down.&lt;/p&gt;

&lt;p&gt;For the platform layer underneath the architecture, see our &lt;a href="https://dev.to/blog/whatsapp-business-api-india-2026-guide/"&gt;WhatsApp Business API guide&lt;/a&gt; and the &lt;a href="https://dev.to/blog/whatsapp-crm-india-2026-setup-cost-features/"&gt;WhatsApp CRM setup post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Queue: Mint the Idempotency Key Before the Provider Call
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mint the idempotency key before the provider call, not after the response.&lt;/strong&gt; Everyone gets this backwards. If you wait for the provider to hand you a message ID and then store it, the window where you have already sent but recorded nothing is exactly the window where the timeout happens. Key on &lt;code&gt;(event_id, channel, recipient)&lt;/code&gt; with a unique constraint in Postgres, insert first, send second. The insert is your permission slip to call the provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A duplicate OTP is worse than a missing one&lt;/strong&gt;, which is why this matters more here than in most systems. The second SMS invalidates the first, so the user is now typing a code your backend has already retired, and they blame your app, not your gateway. Provider timeouts and cross-provider failover are precisely the conditions that produce double sends, so failover without a claimed key is a bug generator. Same discipline we wrote up in &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks, idempotency and reconciliation&lt;/a&gt;, and the same one we use for OCPP transaction dedupe on PlugEV: &lt;a href="https://dev.to/blog/ocpp-2-0-1-security-india-ev-charging-operators/"&gt;OCPP 2.0.1 security and transaction reconciliation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retries need a policy, not a for-loop.&lt;/strong&gt; Exponential backoff with jitter, capped attempts, and a dead letter queue a human actually opens on Monday. Split retryable failures (5xx, connection timeout, gateway 429) from terminal ones (template rejected, invalid number, scrubber block). Retrying a scrubber rejection just burns INR credits at machine speed and teaches you nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OTP gets its own rules.&lt;/strong&gt; Rate limit per recipient and per IP. Treat a resend inside the validity window as an idempotent read of the existing OTP, not a fresh mint. The user pressing "Resend" three times should cost you one SMS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BullMQ on Redis is an honest default&lt;/strong&gt; at Indian SMB volume. In our own load tests on these builds it stops being enough somewhere past sustained five-figure messages per minute, or when you need replayable event history across teams, or when Redis memory becomes your ceiling. Most startups reaching for Kafka on day one are buying ops load, not throughput.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider Adapters: Making MSG91, Kaleyra, and Exotel Swappable Rungs
&lt;/h2&gt;

&lt;p&gt;Your gateway will never write this section. Their docs are built to make their SDK the center of your notification stack, because a vendor whose API is one rung in a ladder you own is a vendor you can replace. That is exactly what we build for clients, and it is the part of the architecture that has the clearest commercial payoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The adapter interface is deliberately small: &lt;code&gt;send(templateKey, vars, recipient)&lt;/code&gt; returns a &lt;code&gt;providerMessageId&lt;/code&gt;.&lt;/strong&gt; Everything DLT-specific resolves in your layer before the adapter is called. Entity ID, template ID, registered header, and category come out of your template registry, not out of vendor config. The adapter translates your normalized payload into the vendor's request shape and does nothing else. No retries, no logging decisions, no template lookups. In our builds, if an adapter file is longer than roughly 150 lines, DLT logic has leaked into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually differs between vendors is narrower than it looks.&lt;/strong&gt; MSG91, Kaleyra, Exotel, and 2Factor diverge on four things: auth scheme (header key vs bearer vs query param), parameter naming (&lt;code&gt;template_id&lt;/code&gt; vs &lt;code&gt;dlt_template_id&lt;/code&gt; vs &lt;code&gt;tid&lt;/code&gt;), DLR webhook payload shape, and error taxonomy. The first three are mechanical. The fourth is where teams bleed. Normalize every vendor error into your own enum at the adapter edge, something like &lt;code&gt;INVALID_TEMPLATE&lt;/code&gt;, &lt;code&gt;DND_BLOCKED&lt;/code&gt;, &lt;code&gt;RATE_LIMITED&lt;/code&gt;, &lt;code&gt;TRANSIENT&lt;/code&gt;, &lt;code&gt;AUTH_FAILED&lt;/code&gt;. Your retry policy then gets written once, against your enum, instead of five times against five vendors' string codes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failover works because the idempotency key spans providers, not attempts.&lt;/strong&gt; When provider A times out at the socket, the retry through provider B carries the identical key. Your outbox already knows a send is in flight for that key, so the second attempt is a continuation, not a new message. Without this, every failover is a double-OTP and an angry support ticket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest caveat: swapping providers still means re-binding templates through the new telemarketer chain.&lt;/strong&gt; A pure re-bind of already-approved templates through the delivery telemarketer's regulatory team is typically thirty to sixty minutes to a few hours, not seconds. A template that has to be re-registered rather than re-bound is one to three days on top. Either way it is not the config-flag switch vendors imply. Anyone selling you instant provider migration is selling a fantasy. Build the adapter layer anyway. Two things make it worth it. You get real leverage in rate negotiations, because a vendor who knows you can leave prices differently than one who knows you cannot. And you get an exit that exists, which matters the week your provider has an outage during a sale.&lt;/p&gt;

&lt;h2&gt;
  
  
  DLRs Are an Accounting System, Not a Dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Treat delivery receipts like a ledger, not a webhook you eyeball.&lt;/strong&gt; The handler does three things: verify the signature or shared secret, write the raw payload to a receipts table, return 200. Everything else happens in a worker. A DLR handler that takes 4 seconds because it updates six rows and pings Slack will make the provider retry, and now the data you built the whole system to trust is polluted with duplicates you caused yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dedupe on the provider's message ID and never trust ordering.&lt;/strong&gt; DLRs come out of order, arrive twice, and occasionally show up for a message you sent last Tuesday. A &lt;code&gt;DELIVERED&lt;/code&gt; landing after a &lt;code&gt;FAILED&lt;/code&gt; for the same ID is normal, not a bug. Store the receipts append-only, then fold them into a current status with an explicit precedence rule, so replaying the table always yields the same answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reconciliation job is where the delivered-but-never-received bugs finally die.&lt;/strong&gt; Run a daily sweep across three columns: what we sent, what the provider says was delivered, and what the user actually did (OTP verified, link clicked, order page opened). Column one versus column two is the vendor's story. &lt;strong&gt;Column two versus column three is the truth&lt;/strong&gt;, and no gateway dashboard will ever show you that gap, because the gateway cannot see your verify events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alert on OTP verify-rate sliced by template and by operator, not raw send counts.&lt;/strong&gt; Sends stay flat while a rejected template quietly drops everything on one operator. Verify-rate cliffs hours before your first support ticket, usually overnight when nobody is watching sends anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set retention deliberately, and do not run one clock for everything.&lt;/strong&gt; Split the table by what the data is. Message bodies and the phone numbers you sent to are personal data under purpose limitation, so erase them once the purpose they were collected for is served. Your processing logs, the send logs and DLR receipts themselves, are a different animal: under the DPDP Rules 2025 they carry a one-year minimum retention for forensic and investigative purposes before you may erase them. So a naive 90-day purge on the receipts table is not a privacy win, it is a compliance bug that can put you under the statutory floor. Do not reach for a single fixed clock. Enforce the two rules separately in the job, and keep the reasoning next to our &lt;a href="https://dev.to/blog/dpdp-act-compliance-india-2026-website-saas-guide/"&gt;DPDP compliance guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Email: The Channel With No DLT, and the Trap That Creates
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Email has no DLT registry, and that is exactly why teams get burned by it.&lt;/strong&gt; No approval queue means no forcing function, so the discipline you built for SMS quietly gets skipped. Then your OTP lands in Promotions, or a receiving MTA rejects it on DMARC policy, and you have the same silent failure as a template mismatch with a different root cause and no gateway dashboard to blame it on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat email as a peer channel, not an escape hatch.&lt;/strong&gt; Same template registry with versioned bodies, same idempotency key so a retry does not double-send, and the DLR equivalent is your bounce and complaint webhooks. Nobody approves your body first, so your registry is the only place drift gets caught.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The deliverability floor is not optional.&lt;/strong&gt; SPF, DKIM, and DMARC alignment on the sending domain, plus Gmail's bulk-sender rules, which bind any sender of 5,000 or more messages a day to Gmail addresses: SPF and DKIM both, DMARC on the sending domain, a one-click unsubscribe on marketing mail, and a user-reported spam rate kept under 0.1 percent (&lt;a href="https://support.google.com/a/answer/81126" rel="noopener noreferrer"&gt;Gmail sender guidelines&lt;/a&gt;). The honest caveat for an Indian OTP sender is that you may sit under 5,000 a day to Gmail specifically, so the bulk tier may not bind you, but SPF and DKIM are expected of every sender regardless of volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email is the right last rung and the wrong first one.&lt;/strong&gt; Cheap, no approval regime, slowest to arrive, least reliable for auth. Never put it above SMS in an OTP ladder.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Build First, and When to Just Use the Vendor SDK
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stage this honestly, because the order decides whether you waste a month.&lt;/strong&gt; Build the template registry and the CI character-match first. It was about a week of work for us on PlugEV, including the fixture set, and it kills the most expensive bug on the list: template drift silently dropping OTPs while your dashboard says everything is green. Second, the idempotent queue, so a retry never double-charges a customer's patience with two OTPs. Third, provider adapters and the reconciliation job. Reconciliation last is deliberate. You cannot reconcile deliveries you are not yet sending correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now the part that argues against our own invoice.&lt;/strong&gt; If you send low volume on one channel through one vendor, use their SDK and go. Seriously. The queue, the registry, the idempotency layer, none of it earns its cost at that size. This architecture starts paying when you have two channels, or two vendors, or real revenue sitting behind a single OTP that has to land in eight seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where you are decides what you need.&lt;/strong&gt; OTPs cratering right now, that is a delivery-failure audit. Already patching and want it to stop recurring, that is an OTP hardening sprint. Pre-launch and scoping, that is a notification service build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Our gateway dashboard says the SMS was delivered, but the customer never received it. What is actually going on?
&lt;/h3&gt;

&lt;p&gt;The dashboard is reporting on the hop it can see, which is usually the handoff to the operator, not the handset. The three usual culprits are template drift (an edited body no longer matches the registered template, so the operator's scrubber drops it), a category mismatch (a transactional OTP riding a promotional header, filtered for every DND subscriber), or a fallback rung that never fired because the provider returned a soft success. The only way to know which one is to compare what you sent against what the user actually did, which means a reconciliation job, not a dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do we need a separate DLT template and header for the SMS fallback when a WhatsApp message fails?
&lt;/h3&gt;

&lt;p&gt;Yes, and you need it approved before the incident, not during it. The WhatsApp rung is approved by Meta under its utility template rules; the SMS rung below it is approved through the DLT portal under TRAI's regime. They are two different templates governed by two different bodies, and a new content template is typically one to three working days to approve, with a header you do not already have adding one to two more. If you build the ladder without a registered fallback template and a registered Service Implicit header, your gateway will reject every send at exactly the moment you need it. Unless you are a bank, that fallback rides Service Implicit, not Transactional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can we switch SMS providers (say MSG91 to Kaleyra) without redoing our DLT registration and templates?
&lt;/h3&gt;

&lt;p&gt;Your entity ID, header, and approved template bodies stay yours. A pure re-bind of those templates through the new telemarketer chain is typically thirty to sixty minutes to a few hours, and any template that has to be re-registered rather than re-bound is one to three days on top. Anyone promising instant provider migration is selling a fantasy. What the adapter layer buys you is that the switch is a config change plus a binding wait, not a rewrite of every controller that sends a message. That is worth building for the rate leverage alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed with DLT variable pre-tagging in 2026, and do our older templates still work?
&lt;/h3&gt;

&lt;p&gt;Pre-tagging itself is not new: TRAI directed it in May 2023 and it is written into Schedule-I of the TCCCPR 2018. The &lt;a href="https://www.trai.gov.in/sites/default/files/2025-11/Directions_18112025_0.PDF" rel="noopener noreferrer"&gt;18 November 2025 Direction&lt;/a&gt; exists because TRAI recorded that the earlier directions had not been implemented, so it universalised tagging to every variable field and attached a hard rejection date. Clause 8(c) requires new content templates registered after ten days from the 18 November 2025 issue to be pre-tagged, and clause 8(d) gives older templates sixty days from the start of your operator's variable-tag scrubbing, not from any fixed calendar date. Gateways reported scrubbing beginning around mid-January 2026, which put the migration deadline near mid-March 2026, so confirm your operator's actual scrubbing start date. In practice every variable slot now carries a declared type on the operator's side (#numeric#, #alphanumeric#, #url#, #urlott#, #cbn#, #email#, with Annexure-I writing the first as "#number# / #numeric#"). If your older templates were migrated, they work; if they were not, they fail at the scrubber with nothing useful in your logs. Note that clause 8(e) mandates a sixty-day logger mode from the start of scrubbing, during which failing messages are still delivered after generating fault messages, so a template can look fine right up to the day the grace ends and it starts being rejected outright. Put the types in your template registry and validate at the boundary so the failure happens in your process instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it worth building our own notification layer, or should we just use our gateway's SDK directly?
&lt;/h3&gt;

&lt;p&gt;If you send low volume on one channel through one vendor, use the SDK and go. The registry, the queue, and the idempotency layer do not earn their cost at that size, and we will tell a client that before quoting them. This architecture starts paying once you have two channels, or two vendors, or real revenue sitting behind an OTP that has to land in eight seconds. The one piece we would build early regardless is the template registry with a CI character-match, because it was about a week of work for us on PlugEV and it kills the most expensive bug on the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we stop retries and provider failover from sending the same OTP to a customer twice?
&lt;/h3&gt;

&lt;p&gt;Mint the idempotency key before the provider call, not after the response, and key it on &lt;code&gt;(event_id, channel, recipient)&lt;/code&gt; with a unique constraint in Postgres. Insert first, send second, so the row is your permission slip to call the provider. When provider A times out at the socket and you fail over to provider B, the retry carries the identical key, so your outbox recognises it as a continuation rather than a new message. Also treat a user pressing "Resend" inside the validity window as a read of the existing OTP, not a fresh mint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest summary
&lt;/h2&gt;

&lt;p&gt;None of this is clever. It is a registry, a queue, a thin adapter, and a job that checks whether the thing you sent actually landed. The reason it is rare is not difficulty, it is that no vendor has a reason to sell it to you, so it gets built in fragments during incidents and never gets a name.&lt;/p&gt;

&lt;p&gt;If your OTPs are failing right now, start with the reconciliation query, not the refactor. Find the gap between what your provider says it delivered and what your users actually verified. That number tells you which of the three failure modes you have, and it usually takes an afternoon.&lt;/p&gt;

&lt;p&gt;If you would rather have someone who has done this a few times look at it with you, we do delivery-failure audits, OTP hardening sprints, and full notification service builds. Flat INR quotes, GST invoice, no fear-selling. &lt;a href="https://dev.to/contact/"&gt;Get in touch&lt;/a&gt;, or run the cost calculator if you want a build estimate first.&lt;/p&gt;

</description>
      <category>dltcompliantnotificationservic</category>
      <category>notificationservicearchitectur</category>
      <category>dlttemplatescrubbingfailure</category>
      <category>otpdeliveryfailureindia</category>
    </item>
    <item>
      <title>Zoho Books Integration in India (2026): Connect Your Website, Store, or CRM to Your Books, What It Costs, and Build vs Buy</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Tue, 14 Jul 2026 09:23:37 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/zoho-books-integration-in-india-2026-connect-your-website-store-or-crm-to-your-books-what-it-h2</link>
      <guid>https://dev.to/buildbyravirai/zoho-books-integration-in-india-2026-connect-your-website-store-or-crm-to-your-books-what-it-h2</guid>
      <description>&lt;p&gt;Most Indian SMBs do not have a Zoho Books problem. They have a gap between the system that takes orders and the system that keeps the accounts, and a person stuck filling that gap by hand. The store sells all day. Zoho Books holds the real books, the GST returns, the invoices the CA signs off. Between them sits someone with two browser tabs, copying order details from one into the other.&lt;/p&gt;

&lt;p&gt;That copy-paste job is cheap at ten orders a day and expensive at eighty. It is where wrong GSTINs creep in, where invoices go missing, and where the month-end numbers stop tying out. "Zoho Books integration" is just the fix: wire the two systems so an order becomes an invoice, a payment gets recorded against it, contacts stay in sync, and IRN details land back in the record, with nobody re-keying any of it.&lt;/p&gt;

&lt;p&gt;This guide covers how Zoho Books actually connects (its cloud REST API and webhooks), what you can realistically sync, the three ways to build it, honest 2026 rupee costs, a real order-to-invoice build we shipped, and how to scope the work before you pay anyone. We integrate both Zoho Books and Tally, so the advice here is about fit, not brand loyalty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this comes up: your store sells, Zoho Books keeps the books, nobody connects the two
&lt;/h2&gt;

&lt;p&gt;Your storefront takes orders all day. Zoho Books holds the actual accounts, the GST returns, the invoices your CA signs off on. Between them sits a person with two browser tabs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The re-keying tax.&lt;/strong&gt; Someone opens the store or CRM, reads an order, then retypes it into Zoho Books as an invoice. Customer name, GSTIN, line items, HSN codes, tax split. It works fine at ten orders a day. At eighty it breaks: wrong GSTINs, missed invoices, numbers that do not match at month end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who feels it worst.&lt;/strong&gt; D2C and e-commerce stores pushing volume off Shopify or WooCommerce. B2B distributors raising large invoices with strict GSTIN requirements. Subscription SaaS billing on renewal. Service firms running a CRM (Zoho CRM, HubSpot, Pipedrive) right next to Zoho Books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What people actually mean by "Zoho Books integration"&lt;/strong&gt; is usually five flows: an order becomes an invoice, payments received get marked against it, contacts sync both ways, inventory counts down, and e-invoice/IRN details land back in the record. Nobody re-keying any of it.&lt;/p&gt;

&lt;p&gt;Two things this is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One, this is not accepting payments.&lt;/strong&gt; Collecting the money is a separate job (see our &lt;a href="https://dev.to/blog/accept-payments-online-india-2026-upi-cards-autopay-costs/"&gt;accept payments in India&lt;/a&gt; and &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay integration&lt;/a&gt; posts). Integration here means booking the record into the books after the money has already moved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two, this is not rebuilding the e-invoice portal.&lt;/strong&gt; Zoho is a registered GSP and files IRNs for you (we cover that in our &lt;a href="https://dev.to/blog/gst-e-invoicing-irp-irn-developer-guide-india-2026/"&gt;GST e-invoicing&lt;/a&gt; post). You are wiring your systems to Zoho, not to the IRP.&lt;/p&gt;

&lt;p&gt;The audience is large because Zoho Books is one of the most widely adopted GST accounting tools for Indian SMBs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Zoho Books actually connects: OAuth 2.0 REST API and webhooks, in plain words
&lt;/h2&gt;

&lt;p&gt;Zoho Books is &lt;strong&gt;cloud SaaS&lt;/strong&gt;, so you connect to it the same way you connect to any web service: over HTTPS, sending and receiving &lt;strong&gt;JSON through a REST API&lt;/strong&gt;. There is no local server to install and nothing that has to stay open on an office desktop. This is the clean break from Tally, where the machine running it usually has to be switched on and reachable. With Zoho Books, your website or app just makes an API call from wherever it is hosted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch the data center.&lt;/strong&gt; An Indian Zoho org lives on the &lt;strong&gt;.in&lt;/strong&gt; domain. That means you authenticate against &lt;code&gt;accounts.zoho.in&lt;/code&gt; and hit the India API domain (&lt;code&gt;www.zohoapis.in&lt;/code&gt;), not the &lt;code&gt;.com&lt;/code&gt; ones. Using &lt;code&gt;.com&lt;/code&gt; credentials against an &lt;code&gt;.in&lt;/code&gt; org is the single most common first-day mistake we see, and it fails in confusing ways because the login looks fine but the tokens do not work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OAuth 2.0, minus the jargon.&lt;/strong&gt; In the Zoho API console you register either a &lt;strong&gt;self client&lt;/strong&gt; (for a single backend) or a &lt;strong&gt;server-based app&lt;/strong&gt;. You exchange that for an &lt;strong&gt;access token&lt;/strong&gt; (short lived) and a &lt;strong&gt;refresh token&lt;/strong&gt; (long lived, used to silently mint new access tokens). Ask only for the &lt;strong&gt;scopes&lt;/strong&gt; you actually need, for example &lt;code&gt;ZohoBooks.invoices.CREATE&lt;/code&gt;, not blanket full access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;organization_id is mandatory.&lt;/strong&gt; Nearly every call needs the &lt;code&gt;organization_id&lt;/code&gt; of the specific Zoho Books org you are writing to. Your middleware has to store and pass this, especially if one client runs multiple GST entities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webhooks keep things in sync.&lt;/strong&gt; Instead of hammering the API to ask "any new invoices yet?", you register a webhook so Zoho Books &lt;strong&gt;pushes events to you&lt;/strong&gt; when something happens (invoice created, payment recorded). That is how two systems stay current in near real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Respect the limits.&lt;/strong&gt; API calls draw on &lt;strong&gt;daily credits and rate limits&lt;/strong&gt; that are capped per plan. A naive "sync everything on a loop" job will burn through them fast, so design for incremental sync and webhooks from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can actually sync (the real integration jobs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Contacts and customers.&lt;/strong&gt; Every integration starts here. When your site or CRM creates a customer, push them into Zoho Books with the &lt;strong&gt;GSTIN&lt;/strong&gt;, &lt;strong&gt;billing state&lt;/strong&gt;, and &lt;strong&gt;place of supply&lt;/strong&gt; set correctly. Get the place of supply wrong and the tax computes wrong: intra-state throws CGST plus SGST, inter-state throws IGST. We map the shipping state to place of supply and validate the GSTIN format before the call, so bad data never hits your books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sales orders and invoices.&lt;/strong&gt; This is the core of connecting a website to Zoho Books. A paid order on Shopify, WooCommerce, or a custom app becomes a Zoho Books &lt;strong&gt;invoice&lt;/strong&gt; automatically, with the right line items, HSN codes, and tax rates. Trigger on payment success, not cart creation, so you never invoice an abandoned order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payments received and reconciliation.&lt;/strong&gt; Recording the invoice is half the job. We then log the &lt;strong&gt;payment received&lt;/strong&gt; against that invoice and match it to the gateway settlement (Razorpay, Cashfree, PayU) so your Zoho Books balance ties out to what actually landed in your bank after fees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Items and inventory.&lt;/strong&gt; Pushing stock levels and prices one way is straightforward. &lt;strong&gt;Two-way inventory sync is where most projects get messy&lt;/strong&gt;: two systems both claiming to be the source of truth, race conditions, oversells. Pick one master and treat the other as read-only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-invoicing and IRN.&lt;/strong&gt; You do not build IRP integration from scratch. Zoho Books is a registered &lt;strong&gt;GSP&lt;/strong&gt;, so you push a clean invoice and it generates the &lt;strong&gt;IRN&lt;/strong&gt; and signed &lt;strong&gt;QR code&lt;/strong&gt; for you (contrast our from-scratch e-invoicing post, which is far more work). Your job is sending correct data, not talking to the IRP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The extras teams forget.&lt;/strong&gt; Budget for &lt;strong&gt;credit notes&lt;/strong&gt; (returns and refunds), &lt;strong&gt;e-way bills&lt;/strong&gt;, &lt;strong&gt;recurring invoices&lt;/strong&gt; for subscriptions, and &lt;strong&gt;TDS/TCS&lt;/strong&gt; fields. A typical connector build runs roughly Rs 40,000 to Rs 90,000 for a simple one-way build, and Rs 1.5L to Rs 4L+ for a full two-way build, depending on how many of these you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three ways to connect: prebuilt app, iPaaS, or custom API middleware
&lt;/h2&gt;

&lt;p&gt;There are really only three ways to get your website, store, or CRM talking to Zoho Books. Pick by how standard your setup is, not by what looks cheapest on day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Prebuilt marketplace connectors.&lt;/strong&gt; Zoho and third parties publish ready apps for &lt;strong&gt;Shopify&lt;/strong&gt;, &lt;strong&gt;WooCommerce&lt;/strong&gt;, and similar platforms. This is the fastest path when your store is standard and your fields map cleanly: order comes in, invoice gets created, contact syncs. Setup can be a same-day job. But a prebuilt connector is just someone else's middleware. You inherit their field mapping, their sync rules, and their roadmap. If they do not support your GST edge case, you wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. iPaaS and low-code.&lt;/strong&gt; &lt;strong&gt;Zoho Flow&lt;/strong&gt; (bundled with some Zoho One plans), &lt;strong&gt;Zapier&lt;/strong&gt;, and &lt;strong&gt;Make&lt;/strong&gt; let you wire a website to Zoho Books with clicks. Pricing is per task or per operation, so a low-volume flow can run for a few thousand rupees a month. Good for simple, one-directional flows: form submission to contact, order to draft invoice. It gets expensive and fragile once you need branching logic, retries, or high order volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Custom middleware on Node or Laravel.&lt;/strong&gt; This is our default when the flow is non-standard. You get full control over field mapping, retry and idempotency logic, and the &lt;strong&gt;GST handling&lt;/strong&gt; that actually matters here: place-of-supply, CGST/SGST vs IGST split, HSN codes, and IRN/e-invoice sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest fit test:&lt;/strong&gt; standard store plus standard fields leans prebuilt or iPaaS. Custom logic, high volume, or multiple systems leans custom.&lt;/p&gt;

&lt;p&gt;We usually reach for custom on Indian SMB jobs for three reasons: &lt;strong&gt;odd GST cases&lt;/strong&gt; the connectors do not model, &lt;strong&gt;multiple stores feeding one Zoho org&lt;/strong&gt;, and pipelines where &lt;strong&gt;CRM plus store plus books&lt;/strong&gt; all need to stay in sync. When any of those are true, the "buy" option quietly turns into a build anyway, just on someone else's terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build vs buy: the honest economics
&lt;/h2&gt;

&lt;p&gt;There are three real paths, and the right one depends on how weird your process is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buy a connector.&lt;/strong&gt; A ready-made app from the Zoho Marketplace or a third party runs roughly &lt;strong&gt;Rs 1,000 to Rs 4,000 per month&lt;/strong&gt; and you can be live in a day or two. The catch: it books orders the way the vendor assumes you work. If your GST logic, SKU mapping, or invoice numbering does not match their template, you are stuck editing entries by hand every day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use an iPaaS.&lt;/strong&gt; Tools like Zapier, Make, or Pabbly connect your store to Zoho Books on a subscription plus a task or operation limit. Cheap to start (&lt;strong&gt;Rs 1,500 to Rs 6,000 per month&lt;/strong&gt;), but the bill climbs with volume, and every edge case (partial refunds, credit notes, place-of-supply changes) turns into a fragile multi-step flow that breaks quietly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build custom.&lt;/strong&gt; A direct integration against the Zoho Books API is a one-time cost, usually &lt;strong&gt;Rs 40,000 to Rs 90,000 for a simple one-way build, and Rs 1.5L to Rs 4L+ for a full two-way build&lt;/strong&gt;, depending on scope, plus a small maintenance line for when Zoho or your store updates their API. You own the code and, more importantly, the field mapping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The decision rule we use with clients:&lt;/strong&gt; buy for a standard store with standard fields and simple GST. Build when you have custom logic, two or more systems talking to each other (store plus CRM plus books), or unusual GST handling like SEZ, reverse charge, or mixed HSN rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Judge total cost of ownership, not the sticker price.&lt;/strong&gt; A cheap connector that mis-books GST, wrong place of supply or a tax head split incorrectly, costs you far more at &lt;strong&gt;GSTR-1 and GSTR-3B&lt;/strong&gt; filing time than a build that gets it right the first pass. Fixing a month of bad entries is not free.&lt;/p&gt;

&lt;p&gt;We walk through this same logic for &lt;a href="https://dev.to/blog/tally-integration-with-website-india-2026/"&gt;Tally integration&lt;/a&gt; and for &lt;a href="https://dev.to/blog/how-to-vet-web-development-agency-2026-questions/"&gt;how to vet an integration agency&lt;/a&gt; before you sign anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Zoho Books integration costs in India, 2026 (INR, honest ranges)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with the base subscription.&lt;/strong&gt; You are integrating into a Zoho Books plan, so that cost comes first. As of 2026, Zoho Books India plans run roughly Rs 749 (Standard), Rs 1,499 (Professional), and Rs 2,999 (Premium) per month when billed annually, all exclusive of 18% GST. Prices change, so check Zoho's official India pricing page. The API is the same across paid tiers, but webhook limits and user seats differ, so pick the plan for the feature, not for the integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prebuilt marketplace connector.&lt;/strong&gt; For common jobs like WooCommerce or Shopify to Zoho Books order-to-invoice, a ready connector from Zoho Marketplace or a third party typically costs Rs 1,000 to Rs 4,000 per month. That buys order sync, contact creation, and basic tax mapping. It does not buy custom GST logic or e-invoice edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iPaaS (Zoho Flow, Make, Zapier).&lt;/strong&gt; Budget Rs 1,500 to Rs 6,000 per month, and watch the task math. Every order that fires 3 to 5 steps (create contact, create invoice, record payment) burns 3 to 5 tasks. At 500 orders a month that is 2,500 tasks, which pushes you off the cheap tier fast. Do the multiplication before you commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom API integration (one-time).&lt;/strong&gt; A simple one-way order-to-invoice build runs roughly Rs 40,000 to Rs 90,000. A full two-way build with inventory sync, payment reconciliation, and e-invoice/IRN generation is closer to Rs 1.5L to Rs 4L+, depending on entity count and edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ongoing maintenance and AMC.&lt;/strong&gt; Our typical AMC is about 12 to 18 percent of build cost per year, or a Rs 5,000 to Rs 15,000 per month retainer. Zoho ships API changes and rotates OAuth tokens, so someone has to watch it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden costs buyers miss.&lt;/strong&gt; Refresh-token failures silently stop sync until someone re-authorizes. Rate-limit backoff needs retry queues. Reconciliation reports (what synced versus what did not) are real dev hours. And you need a staging org separate from production, which is a second subscription most people forget to budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real build: order-to-invoice plus payment sync for a D2C store
&lt;/h2&gt;

&lt;p&gt;Here is a build we shipped for a Shopify store on Razorpay. The pattern holds for WooCommerce or a custom checkout too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The flow.&lt;/strong&gt; The store fires an &lt;code&gt;order/paid&lt;/code&gt; webhook. Your middleware (a small Node or Laravel service) does not talk to Zoho Books right then. It validates the signature, drops the event on a queue (Redis or SQS), and returns 200 fast. A worker picks it up and writes to Zoho Books. Keep the webhook handler dumb and the worker smart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency first.&lt;/strong&gt; Store the Razorpay &lt;code&gt;payment_id&lt;/code&gt; (or Shopify order id) with a unique constraint before you process. A retried webhook then hits the same key and exits early instead of raising a second invoice. We wrote up the webhook and reconciliation details in our Razorpay webhooks post, and the same discipline applies here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contact mapping.&lt;/strong&gt; Before the invoice, find-or-create the customer in Zoho Books. Match on email or phone, then set &lt;strong&gt;GSTIN&lt;/strong&gt; and &lt;strong&gt;place of supply&lt;/strong&gt; on the contact. Place of supply decides CGST+SGST vs IGST, so a blank value here quietly produces wrong tax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoice and payment.&lt;/strong&gt; Create the invoice, then record a payment against it (customer payment linked to that invoice), and write the returned Zoho &lt;code&gt;invoice_id&lt;/code&gt; back onto your order row. That id is your audit trail when finance asks why numbers differ.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliability plumbing.&lt;/strong&gt; Retries with exponential backoff, a dead-letter queue for events that fail after a set number of attempts (5 in this build), structured JSON logs with the order id on every line, and a daily reconciliation job that counts store orders against Zoho invoices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it breaks in production.&lt;/strong&gt; The OAuth access token expires every hour, so refresh it. You will hit Zoho API credit limits on big sale days. Blank place of supply gives wrong GST. And a webhook is delivered at least once, never exactly once, so idempotency is not optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zoho Books vs Tally: which one to integrate with
&lt;/h2&gt;

&lt;p&gt;The two systems are wired in opposite ways, and that shapes everything about the integration. &lt;strong&gt;Zoho Books is cloud-native&lt;/strong&gt;: you talk to it over a REST API with OAuth tokens, and it can push events back to you through webhooks. &lt;strong&gt;Tally is a desktop XML gateway&lt;/strong&gt;: you POST XML requests to a local machine on &lt;strong&gt;port 9000&lt;/strong&gt; and read XML back. If you want the full picture on the Tally side, we wrote it up here: &lt;a href="https://dev.to/blog/tally-integration-with-website-india-2026/"&gt;Tally integration with your website or custom software&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That difference drives &lt;strong&gt;availability&lt;/strong&gt;. Cloud Zoho Books answers 24x7 from anywhere. On-prem Tally is only reachable while that specific PC is switched on, TallyPrime is open, and it sits on the same network or a tunnel. Miss any of those and the sync silently stalls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ecosystem pull&lt;/strong&gt; favours Zoho if you already live there. Zoho Books snaps into Zoho CRM, Zoho Inventory, and Zoho Flow with almost no glue code, which is why &lt;strong&gt;Zoho Books CRM integration&lt;/strong&gt; is a common ask from Indian SMBs running the Zoho One suite.&lt;/p&gt;

&lt;p&gt;But be honest about the &lt;strong&gt;install base&lt;/strong&gt;. Tally remains very widely used for Indian accounting, and your CA probably files GST from it. The right answer is usually whichever system your accountant already trusts, because that is where the real books live.&lt;/p&gt;

&lt;p&gt;This is not a religious war. &lt;strong&gt;We integrate both.&lt;/strong&gt; The decision is about where your data of record sits today, not brand loyalty. One more thing: when a team &lt;strong&gt;migrates Tally to Zoho Books&lt;/strong&gt;, the integration surface gets simpler (one cloud API, no local machine to babysit). We flag that when the migration effort is worth it and when it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes and GST edge cases we see
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wrong data center domain.&lt;/strong&gt; Indian Zoho accounts live on &lt;code&gt;.in&lt;/code&gt;, not &lt;code&gt;.com&lt;/code&gt;. If your token endpoint says &lt;code&gt;accounts.zoho.com&lt;/code&gt; but your org is on &lt;code&gt;www.zohoapis.in&lt;/code&gt;, every call 401s and you burn an afternoon blaming the scopes. Match the domain to where the account was created, and store it as config so staging and prod can differ.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token handling.&lt;/strong&gt; The access token expires in about &lt;strong&gt;1 hour&lt;/strong&gt;. We still inherit projects where someone pasted a live token into code. It works in the demo, then dies overnight. Store the &lt;strong&gt;refresh token&lt;/strong&gt; securely, refresh on 401, and never commit either to git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No idempotency.&lt;/strong&gt; Zoho retries webhooks, and your queue will replay too. Without an idempotency key (map your order ID to the Zoho invoice ID before you create it), one order becomes three invoices in the books. Check-then-create, or use the reference number field as a guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Place of supply.&lt;/strong&gt; This is the one that quietly corrupts &lt;strong&gt;GST returns&lt;/strong&gt;. If place of supply is derived wrong, Zoho stamps &lt;strong&gt;IGST&lt;/strong&gt; where it should be &lt;strong&gt;CGST/SGST&lt;/strong&gt; (or the reverse). The invoice looks fine; GSTR-1 does not. Set place of supply from the shipping state, not the billing state, and test an intra-state and inter-state order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API credits and rate limits.&lt;/strong&gt; A bulk backfill of a year's orders hits the daily API credit cap and stops halfway, leaving books half-synced. Throttle to a few calls per second, batch, and run backfills off-peak with a resume cursor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hard cases a generic connector never asks about.&lt;/strong&gt; &lt;strong&gt;Reverse charge&lt;/strong&gt;, &lt;strong&gt;TCS&lt;/strong&gt; on marketplace sales, rupee &lt;strong&gt;rounding&lt;/strong&gt; on line totals, and &lt;strong&gt;HSN&lt;/strong&gt; codes that do not match your Zoho item master. Map these explicitly. A ready connector will silently skip them, and you find out at filing time. In our experience, budget &lt;strong&gt;Rs 15,000 to Rs 45,000&lt;/strong&gt; of dev time for these edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to scope and hire this before you pay anyone
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Answer four questions first.&lt;/strong&gt; Which systems are you connecting (website, WooCommerce or Shopify store, custom app, Zoho CRM)? Which direction does data flow, one-way (orders push into Zoho Books) or two-way (invoice and payment status flow back)? What is your real volume, say 20 or 2,000 orders a day? And which fields must map: SKU, HSN code, GST rate, place of supply, customer GSTIN? Get these on one page before anyone quotes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to hand a developer.&lt;/strong&gt; Five to ten real sample orders (not dummy data), your Zoho org ID and edition, your GST setup (registered states, composition or regular), and the ugly edge cases: partial refunds, COD, cancelled orders, multi-state shipping, e-invoice/IRN generation above the turnover threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Red flags in a quote.&lt;/strong&gt; No mention of &lt;strong&gt;idempotency&lt;/strong&gt; (so a webhook retry does not create duplicate invoices), no talk of the &lt;strong&gt;.in data center&lt;/strong&gt; (Zoho India accounts live on zohoapis.in, not .com), no reconciliation plan for payments, or a flat price with zero discovery. Any of those means they will learn on your money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How we do it.&lt;/strong&gt; Discovery call, a written field-mapping doc, build against a Zoho staging org, then production with logging and alerts so failed syncs are visible, not silent. The custom middleware sits between your store and Zoho as its own service, built on &lt;strong&gt;Node or Laravel&lt;/strong&gt;, following our &lt;a href="https://dev.to/blog/rest-api-design-best-practices-india-2026/"&gt;REST API design best practices&lt;/a&gt;. Typical build runs roughly Rs 40,000 to Rs 90,000 for a simple one-way build, and Rs 1.5L to Rs 4L+ for a full two-way build, depending on scope.&lt;/p&gt;

&lt;p&gt;Send us your store and Zoho setup. We will tell you honestly whether to buy a connector or build one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much does it cost to integrate Zoho Books with my website or app in India?
&lt;/h3&gt;

&lt;p&gt;It depends on the path. A prebuilt marketplace connector runs about Rs 1,000 to Rs 4,000 per month, an iPaaS flow (Zapier, Make, Zoho Flow) about Rs 1,500 to Rs 6,000 per month with the bill rising as order volume climbs, and a one-time custom build runs roughly Rs 40,000 to Rs 90,000 for a simple one-way order-to-invoice job and Rs 1.5L to Rs 4L+ for a full two-way build with inventory and e-invoice sync. On a custom build, also plan for AMC of about 12 to 18 percent of build cost per year. Judge total cost of ownership, because a cheap connector that mis-books GST costs more at filing time than a build that gets it right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I automatically sync my e-commerce store orders to Zoho Books as invoices?
&lt;/h3&gt;

&lt;p&gt;Yes, and it is the most common job we build. A paid order on Shopify, WooCommerce, or a custom checkout becomes a Zoho Books invoice with the correct line items, HSN codes, and tax rates. The key rule is to trigger on payment success, not on cart creation, so abandoned carts never raise an invoice. Set GSTIN and place of supply on the contact first, or the tax split (CGST/SGST vs IGST) comes out wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I buy a ready-made Zoho Books connector or build a custom API integration?
&lt;/h3&gt;

&lt;p&gt;Buy when your store is standard, your fields map cleanly, and your GST is simple. Build when you have custom logic, two or more systems that must stay in sync (store plus CRM plus books), or unusual GST handling like SEZ, reverse charge, or mixed HSN rates. A prebuilt connector is just someone else's middleware, so you inherit their field mapping and their roadmap. If any of the "build" conditions are true, the buy option often turns into a build anyway, just on the vendor's terms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Zoho Books handle GST e-invoicing and IRN generation, or do I need a separate integration for that?
&lt;/h3&gt;

&lt;p&gt;Zoho Books is a registered GSP, so it generates the IRN and signed QR code for you. You do not build integration to the government IRP from scratch. Your job is to push a clean, correct invoice (right GSTIN, place of supply, HSN codes) and let Zoho handle the IRN. That is far less work than the from-scratch e-invoicing route we cover in our separate GST e-invoicing post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zoho Books vs Tally: which one is easier to connect to my custom software?
&lt;/h3&gt;

&lt;p&gt;Zoho Books is easier to connect in pure technical terms. It is cloud-native, so you talk to a REST API with OAuth tokens and receive webhooks, and it answers 24x7 from anywhere. Tally is a desktop XML gateway on port 9000, reachable only while that specific PC is on, TallyPrime is open, and the network or tunnel is up. That said, integrate whichever system holds your real books, because your CA probably files GST from it, and we build for both.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does a custom Zoho Books API integration take to build and go live?
&lt;/h3&gt;

&lt;p&gt;Typically, in our experience, a simple one-way order-to-invoice build is 1 to 2 weeks including testing against a staging org. A full two-way build with inventory sync, payment reconciliation, and e-invoice/IRN generation is closer to 4 to 8 weeks, depending on entity count and edge cases. Timelines stretch when GST edge cases (reverse charge, TCS, multi-state shipping) or multiple source systems are in scope. Most of the delay is discovery and edge-case mapping, not the core code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest bottom line
&lt;/h2&gt;

&lt;p&gt;There is no single right answer here, only the right fit for how your business actually works. If your store is standard and your GST is simple, buy a connector and move on. If your process has real edge cases, or your books, store, and CRM all need to agree, a custom build usually pays for itself the first filing season it saves you. The expensive mistake is picking on sticker price and finding out at GSTR-1 time that the numbers do not tie out.&lt;/p&gt;

&lt;p&gt;If you are not sure which side of that line you fall on, tell us your store, your Zoho setup, and your volume. We will give you a straight read on whether to buy or build. Start at &lt;a href="https://dev.to/contact/"&gt;/contact/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>zohobooksintegration</category>
      <category>zohobooksapiintegration</category>
      <category>connectwebsitetozohobooks</category>
      <category>syncecommerceorderstozohobooks</category>
    </item>
    <item>
      <title>OCPP 2.0.1 Security for Indian EV Charging Operators (2026): TLS/mTLS, OCSP Revocation, Offline Authorization, and Transaction Reconciliation</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:07:27 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/ocpp-201-security-for-indian-ev-charging-operators-2026-tlsmtls-ocsp-revocation-offline-41kk</link>
      <guid>https://dev.to/buildbyravirai/ocpp-201-security-for-indian-ev-charging-operators-2026-tlsmtls-ocsp-revocation-offline-41kk</guid>
      <description>&lt;p&gt;Securing an OCPP 2.0.1 network in India is less about the spec PDF and more about what your chargers do at 11pm when the 4G link drops and a stranger plugs in. Get the transport layer right and you have encrypted, authenticated sessions that bill correctly. Get it wrong and an unsigned firmware channel or a never-revoked charger certificate becomes a quiet backdoor into your CSMS, your payment flow, and every station you run.&lt;/p&gt;

&lt;p&gt;This is a working operator's guide to the four things that actually decide whether a 2.0.1 network is both secure and paid: TLS and mTLS on the WebSocket, OCSP revocation, offline authorization when connectivity dies, and reconciling those offline sessions into Razorpay without double-charging. We wrote it from running &lt;a href="https://dev.to/products/plugev/"&gt;PlugEV&lt;/a&gt;, a production OCPP 1.6-J and 2.0.1 CSMS with a Go WebSocket gateway, a Laravel admin, and Razorpay billing behind it.&lt;/p&gt;

&lt;p&gt;None of this comes from the spec alone. All of it comes from chargers that dropped, certs that expired, and sessions that tried to bill twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 60-second security answer for a live Indian fleet
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Run Profile 2 as your floor, Profile 3 where the contract demands it.&lt;/strong&gt; OCPP 2.0.1 defines three security profiles. Profile 1 is HTTP Basic over unsecured transport, so skip it. &lt;strong&gt;Profile 2&lt;/strong&gt; is TLS server authentication plus HTTP Basic Auth, and for a public network in India it is the honest default: real transport encryption without the per-charger certificate provisioning headache. &lt;strong&gt;Profile 3&lt;/strong&gt; is mutual TLS (mTLS), where the charger presents its own client certificate. Move to it when a B2B fleet contract or CEA compliance actually requires it, not before.&lt;/p&gt;

&lt;p&gt;Two more problems this post owns. &lt;strong&gt;Offline authorization&lt;/strong&gt; keeps a charger usable when 4G drops, using the cached &lt;code&gt;LocalAuthorizationList&lt;/code&gt; and the &lt;code&gt;AuthCacheCtrlr&lt;/code&gt; component (&lt;code&gt;AuthCacheCtrlr.Enabled&lt;/code&gt;) so a session can start without the CSMS. &lt;strong&gt;Reconciliation&lt;/strong&gt; then replays the queued &lt;code&gt;TransactionEvent&lt;/code&gt; messages so an offline plug-in bills exactly once in Razorpay, no double-charge, no revenue leak.&lt;/p&gt;

&lt;p&gt;Scope: this is how to secure 2.0.1 once you are on it. For whether to migrate at all, see the &lt;a href="https://dev.to/blog/ocpp-2-0-1-migration-playbook-india/"&gt;migration playbook&lt;/a&gt;; for general build notes, the &lt;a href="https://dev.to/blog/ocpp-implementation-guide-ev-charging-csms/"&gt;CSMS build guide&lt;/a&gt;. Every claim here comes from running this on 40+ chargers across 2 cities on a Go WebSocket gateway, not the spec PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three OCPP 2.0.1 security profiles, and which one you should actually run
&lt;/h2&gt;

&lt;p&gt;We just called them a floor and a contract demand. Here is what each profile actually trusts, because they are not "levels of good," they are different trust models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile 1&lt;/strong&gt; is HTTP Basic auth (username plus password) sent over plain &lt;code&gt;ws://&lt;/code&gt;. There is no transport encryption. The charger's password crosses the network in the clear, so anyone on the path can read it and impersonate the station. This should never touch a public charger. We still see it left on in the field more than operators admit, usually because a commissioning engineer got the WebSocket connected on &lt;code&gt;ws://&lt;/code&gt; and nobody went back to harden it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile 2&lt;/strong&gt; is TLS with a server-side certificate (the CSMS proves who it is) plus Basic auth for the charger. Traffic is encrypted, the charger trusts the CSMS, but the charger still authenticates with a password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile 3&lt;/strong&gt; is mutual TLS. Both sides present X.509 certificates, and there is no password. The charger's identity is its client cert, provisioned via &lt;code&gt;SignCertificate&lt;/code&gt; / &lt;code&gt;CertificateSigned&lt;/code&gt; against your PKI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our honest India default:&lt;/strong&gt; Profile 2 is the floor for any public AC or DC charger. Move to Profile 3 when a fleet client, an aggregator, or a CEA/BIS posture wants cryptographic client-cert identity per station.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The downgrade trap:&lt;/strong&gt; if your CSMS accepts plain &lt;code&gt;ws://&lt;/code&gt; on the same endpoint as &lt;code&gt;wss://&lt;/code&gt;, a charger can silently connect on Profile 1 and you will not notice. Terminate &lt;code&gt;ws://&lt;/code&gt; at the load balancer. One endpoint, one profile floor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firmware reality check:&lt;/strong&gt; not every installed model does Profile 3 in 2026. Audit it. Send &lt;code&gt;GetVariables&lt;/code&gt; for the &lt;code&gt;SecurityProfile&lt;/code&gt; variable (component &lt;code&gt;OCPPCommCtrlr&lt;/code&gt;) and read what each station reports.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Profile 1:&lt;/strong&gt; no transport encryption, charger identity by password, no CSMS identity, near-zero operational cost. Never on a public charger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile 2:&lt;/strong&gt; TLS transport encryption, charger identity by password, CSMS identity by a server cert, low operational cost (one server cert to run).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile 3:&lt;/strong&gt; mutual TLS transport encryption, charger identity by its own client cert, CSMS identity by server cert, higher operational cost (PKI, rotation, and OCSP to run).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running your own PKI adds real ops load, so budget for it. In our experience, all in (tooling plus the loaded engineering time to run rotation, revocation, and audits) a self-operated PKI costs roughly INR 3 to 6 lakh per year. Managed tiers cost less in tooling and shift the tradeoff, and we break the tiers down below. Pricing here is indicative for 2026 and reflects our own estimates; confirm current quotes before you budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inside the wss:// handshake: TLS and mTLS on the WebSocket gateway
&lt;/h2&gt;

&lt;p&gt;Those profiles are abstractions until you watch a charger actually connect. When a charger boots, it dials &lt;code&gt;wss://csms.example.in/ocpp/CP-2201&lt;/code&gt; and starts a normal TLS handshake: &lt;strong&gt;ClientHello&lt;/strong&gt;, then our gateway returns its server certificate chain, and only after the TLS session is up does the HTTP &lt;strong&gt;Upgrade&lt;/strong&gt; happen. The subprotocol is negotiated right there in the headers, &lt;code&gt;Sec-WebSocket-Protocol: ocpp2.0.1&lt;/code&gt;. If the charger sends &lt;code&gt;ocpp1.6&lt;/code&gt; and we only speak 2.0.1, the Upgrade fails before a single &lt;code&gt;BootNotification&lt;/code&gt; is sent. Watch that header first when a "working" charger will not connect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Profile 2&lt;/strong&gt; is server-auth TLS. The charger validates our leaf against a trusted root, checks the SAN matches the host, and reads &lt;strong&gt;SNI&lt;/strong&gt; so we can serve the right cert on a shared IP. Chain order matters: leaf, then intermediate, or older embedded clients fail path building. The classic field failure is a charger that trusts any cert because validation is stubbed in firmware. We have seen units happily connect to a self-signed test cert. Test that deliberately before you sign a fleet contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Profile 3&lt;/strong&gt; is mutual TLS. The charger presents its own client certificate, our gateway validates it against the &lt;strong&gt;CPO sub-CA&lt;/strong&gt;, and we map the cert's CN or SAN to a &lt;code&gt;chargePointId&lt;/code&gt;. That identity replaces the Basic-auth password entirely, no shared secret to leak. Running mTLS means running PKI, and an entry managed tier starts around INR 40,000 to 1,20,000 per year in tooling. More on the cost tiers in the next section.&lt;/p&gt;

&lt;p&gt;TLS terminates at the &lt;strong&gt;Go gateway&lt;/strong&gt;. It holds the WebSocket and speaks OCPP over an internal channel to the Laravel admin, which never touches a raw socket. This is why a dumb L4 load balancer breaks you: if it passes TLS straight through, nothing extracts the client-cert identity, so mTLS becomes decoration. Terminate where you can read the cert.&lt;/p&gt;

&lt;p&gt;Posture: enforce &lt;strong&gt;TLS 1.2 minimum&lt;/strong&gt; (1.3 where firmware allows), disable renegotiation on the TLS 1.2 path (TLS 1.3 has no renegotiation to disable), and pin an allowed cipher list because embedded charger stacks are often years old.&lt;/p&gt;

&lt;p&gt;Keepalive uses WebSocket &lt;strong&gt;ping/pong&lt;/strong&gt;. Aggressive idle timeouts on Indian 4G/NB-IoT links drop sessions, and every drop redoes the full TLS handshake, so tune timeouts generously to avoid reconnect storms.&lt;/p&gt;

&lt;h2&gt;
  
  
  PKI you actually have to run: certificate provisioning, rotation, and the ISO 15118 problem
&lt;/h2&gt;

&lt;p&gt;Once you turn on Security Profile 2 or 3, you stop being a charging company and start being a small certificate authority. Plan for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hierarchy you end up owning.&lt;/strong&gt; A CPO root (offline, or an intermediate under a managed CA), then two sub-CAs: one that issues &lt;strong&gt;charger client certs&lt;/strong&gt;, one that anchors the &lt;strong&gt;CSMS server cert&lt;/strong&gt;. Do not sign the server cert directly off your root. If that root key leaks or the server cert has to be reissued in a hurry, you want to revoke an intermediate, not rebuild trust on every charger in the field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provisioning the first cert.&lt;/strong&gt; Either the factory installs a client cert, or you field-provision. In practice the charger boots with a bootstrap cert, sends &lt;strong&gt;SignCertificate&lt;/strong&gt; with a CSR, and the CSMS replies &lt;strong&gt;CertificateSigned&lt;/strong&gt; with the issued chain. To seed or replace CA certs you use &lt;strong&gt;InstallCertificate&lt;/strong&gt;, and &lt;strong&gt;GetInstalledCertificateIds&lt;/strong&gt; / &lt;strong&gt;DeleteCertificate&lt;/strong&gt; to audit what is actually on the box. All of it rides the live OCPP WebSocket, so no truck roll.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rotation without site visits.&lt;/strong&gt; An expired cert on a remote charger is a self-inflicted outage. We schedule reissue well before expiry and alarm any charger whose cert is inside &lt;strong&gt;30 days&lt;/strong&gt; of lapsing. That is our operator choice, not a spec rule: we alarm at 30 days and you tune it to your own reissue lead time, driven off &lt;code&gt;GetInstalledCertificateIds&lt;/code&gt; sweeps, not hope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the three families separate.&lt;/strong&gt; 2.0.1 distinguishes CSMS/server, charger/client, and V2G/ISO 15118 certs. Conflating them is the most common &lt;code&gt;InstallCertificate&lt;/code&gt; failure we see: right cert, wrong &lt;code&gt;certificateType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISO 15118 Plug and Charge in India.&lt;/strong&gt; The V2G root and contract-certificate ecosystem here is still thin, with no established Indian V2G Root CA or contract-certificate operator ecosystem to build against. PnC is design-for-later, not a reason to stand up a full V2G PKI in 2026. Build the hooks, skip the CA. (See our &lt;a href="https://dev.to/blog/ocpp-2-0-1-migration-playbook-india/"&gt;migration playbook&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tooling.&lt;/strong&gt; Use &lt;strong&gt;step-ca&lt;/strong&gt; or a managed PKI over hand-rolled OpenSSL scripts. The cost tiers we see, indicative for 2026: an entry managed tier that handles issuance and OCSP runs roughly INR 40,000 to 1,20,000 per year in tooling; a fuller managed CA tier with HSM-backed key storage, more certificate families, and higher assurance runs roughly INR 1.5 to 4 lakh per year; and running the whole thing yourself, all in with the loaded engineering time to operate it, lands around INR 3 to 6 lakh per year. Once B2B fleet contracts start auditing you, key storage matters: an HSM, or at minimum sealed secrets, never keys sitting in plaintext on disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  OCSP and certificate revocation: proving a cert is still valid
&lt;/h2&gt;

&lt;p&gt;Issuing certs is the easy half. Killing one you no longer trust is the half everyone skips. A charger cert stays cryptographically &lt;strong&gt;valid until its expiry date&lt;/strong&gt;, even if the unit was stolen off a wall or decommissioned last month. If you cannot revoke a cert and then actually &lt;strong&gt;check&lt;/strong&gt; revocation on connect, a lifted charger can keep opening a TLS session to your CSMS for the full cert lifetime. That is the gap.&lt;/p&gt;

&lt;p&gt;You have three tools. &lt;strong&gt;CRLs&lt;/strong&gt; are full revocation lists. A full CRL can be large, and on a charger sitting on a metered mobile SIM, pulling it on every check is painful and eats data you pay for. &lt;strong&gt;OCSP&lt;/strong&gt; asks a responder about one cert only, which is lighter. &lt;strong&gt;OCSP stapling&lt;/strong&gt; is lighter still.&lt;/p&gt;

&lt;p&gt;OCPP 2.0.1 helps here. The charger does not need its own route to a public OCSP responder. It sends &lt;strong&gt;GetCertificateStatus&lt;/strong&gt; with the OCSP request data, and the CSMS relays it to the responder and returns the signed answer. Your CSMS becomes the OCSP proxy, which matters for chargers behind NAT or on a locked-down APN.&lt;/p&gt;

&lt;p&gt;On our gateway we &lt;strong&gt;staple&lt;/strong&gt; the responder answer straight into the TLS handshake. The charger gets a fresh, signed status with no second round trip on a flaky link. We cache staples and refresh before they go stale.&lt;/p&gt;

&lt;p&gt;Then the offline dilemma. If nothing can reach a responder, do you &lt;strong&gt;soft-fail&lt;/strong&gt; (allow) or &lt;strong&gt;hard-fail&lt;/strong&gt; (reject)? On unattended public sites we soft-fail with a short grace window, because hard-fail bricks a whole site when the responder blips.&lt;/p&gt;

&lt;p&gt;Operational must-haves: put &lt;strong&gt;revoke-on-decommission&lt;/strong&gt; in the charger offboarding checklist so a pulled unit is dead the same day, and alert when OCSP responses &lt;strong&gt;go stale&lt;/strong&gt; past your refresh threshold. A stale-staple alarm has caught silent responder outages for us more than once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signed firmware updates: why unsigned OTA is a backdoor
&lt;/h2&gt;

&lt;p&gt;Revocation stops a bad cert. Signed firmware stops a bad image, and it is the higher-stakes control, because firmware is root on the charger. If your OTA channel accepts an unsigned or unverified image, anyone who can reach that channel gets &lt;strong&gt;remote code execution on every charger you operate&lt;/strong&gt;. That is not a bug, it is a backdoor into the whole fleet, and it reaches your CSMS, your OCPP WebSocket, and the payment flows behind them.&lt;/p&gt;

&lt;p&gt;OCPP 2.0.1 fixes the transport with &lt;strong&gt;&lt;code&gt;UpdateFirmware&lt;/code&gt;&lt;/strong&gt;. Its &lt;code&gt;firmware&lt;/code&gt; object carries the firmware location plus a &lt;strong&gt;&lt;code&gt;signingCertificate&lt;/code&gt;&lt;/strong&gt; and a &lt;strong&gt;&lt;code&gt;signature&lt;/code&gt;&lt;/strong&gt;. The charger verifies that signature against a trusted manufacturer or CPO key before it flashes anything. No valid chain, no flash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the CSMS must enforce.&lt;/strong&gt; Reject unsigned images at the source. Verify the signing chain yourself before you ever send the request. Then log every &lt;strong&gt;&lt;code&gt;FirmwareStatusNotification&lt;/code&gt;&lt;/strong&gt; transition (&lt;code&gt;Downloading&lt;/code&gt;, &lt;code&gt;SignatureVerified&lt;/code&gt;, &lt;code&gt;InvalidSignature&lt;/code&gt;, &lt;code&gt;Installing&lt;/code&gt;, &lt;code&gt;InstallRebooting&lt;/code&gt;, &lt;code&gt;Installed&lt;/code&gt;) so a stuck, failed, or downgraded update is visible instead of silent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollout, not blast.&lt;/strong&gt; A bad flash on about 40 chargers at once is a field-visit disaster and lost revenue while sites sit dark. We push to one &lt;strong&gt;canary&lt;/strong&gt; charger, watch it complete a full session, then move in rings. In our experience, recovery visits in tier-2 cities cost real money, roughly INR 1,500 to 4,000 per site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;India field reality.&lt;/strong&gt; Vendor firmware maturity is mixed. Some models advertise signing but do not actually reject a bad signature. We test rejection &lt;strong&gt;per model&lt;/strong&gt; before we trust the mechanism on that hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline authorization: what the charger decides when 4G drops
&lt;/h2&gt;

&lt;p&gt;Everything so far assumes the charger can reach you. Most of our public sites cannot, reliably. They run on a single 4G SIM for backhaul, and in practice that link drops several times a day, sometimes for a few seconds during a tower handover, sometimes for twenty minutes when it rains. A charger that can only authorize online is a charger that stops earning during every one of those windows. So the real question is not "does it support OCPP 2.0.1," it is "what does the charger decide on its own when the CSMS is unreachable."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.0.1 gives you two local mechanisms.&lt;/strong&gt; The &lt;strong&gt;Local Authorization List&lt;/strong&gt; is an operator-pushed allow-list you sync with &lt;code&gt;SendLocalList&lt;/code&gt; and check with &lt;code&gt;GetLocalListVersion&lt;/code&gt;. The &lt;strong&gt;Authorization Cache&lt;/strong&gt; is the charger auto-remembering &lt;code&gt;idTokens&lt;/code&gt; it has already seen the CSMS approve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The decision flow.&lt;/strong&gt; Online, the charger sends &lt;code&gt;Authorize&lt;/code&gt; and waits for the CSMS &lt;code&gt;authorizationStatus&lt;/code&gt;. Offline, behaviour is governed by config. &lt;code&gt;LocalAuthorizeOffline&lt;/code&gt; lets it fall back to the local list, then the cache. &lt;code&gt;LocalPreAuthorize&lt;/code&gt; lets it start energy immediately on a local hit without round-tripping. &lt;code&gt;OfflineTxForUnknownIdEnabled&lt;/code&gt; is the dangerous one: set true, it will start a session for a token it has never seen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a policy call, not a default.&lt;/strong&gt; A fleet depot with 30 known drivers runs a tight local list and turns unknown-token offline start off. An open public charger that authorizes unknown tokens offline is choosing revenue continuity over fraud risk. Choose it deliberately, per site, and write it down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the list fresh or it breaks quietly.&lt;/strong&gt; &lt;code&gt;SendLocalList&lt;/code&gt; supports &lt;code&gt;Full&lt;/code&gt; and &lt;code&gt;Differential&lt;/code&gt; updates against a version number. Push differentials on membership changes, full only on drift. A stale list rejects a valid driver; large local lists can overflow the flash on cheaper AC units.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to the meter.&lt;/strong&gt; On an offline start the charger opens the transaction, stamps &lt;code&gt;transactionId&lt;/code&gt;, and stores its &lt;code&gt;sampledValue&lt;/code&gt; meter readings inside each queued &lt;code&gt;TransactionEventRequest&lt;/code&gt; (eventType Started, Updated, Ended) locally. When the link returns it replays the queue. That replay is the handoff into reconciliation, and where Razorpay either gets billed correctly or leaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transaction reconciliation: settling offline sessions in Razorpay without double-charging
&lt;/h2&gt;

&lt;p&gt;An offline session is a real transaction the CSMS never watched happen. The charger delivered kWh, logged it locally, and only when the WebSocket comes back does it flush a burst of queued &lt;strong&gt;TransactionEventRequest&lt;/strong&gt; messages (&lt;code&gt;eventType&lt;/code&gt; Started, Updated, Ended, each carrying &lt;code&gt;offline: true&lt;/code&gt; and a &lt;code&gt;seqNo&lt;/code&gt;). Those queued events must collapse into exactly one correct charge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency is the whole game.&lt;/strong&gt; We dedupe on the charger-generated &lt;code&gt;transactionInfo.transactionId&lt;/code&gt; plus &lt;code&gt;seqNo&lt;/code&gt;, with a unique constraint in Postgres. A charger that reconnects and retries the same Ended event (common) hits the constraint and no second billing row is born. Same discipline we use on the payment side: see &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks, idempotency and reconciliation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust the meter, not the clock.&lt;/strong&gt; We compute energy from the first and last &lt;code&gt;sampledValue&lt;/code&gt; readings inside the &lt;code&gt;TransactionEvent&lt;/code&gt; stream, never from wall-clock duration. Offline chargers drift; their RTC can be minutes or hours off, so we treat the charger timestamp as advisory and stamp our own received-at time for ordering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The billing sequence we run:&lt;/strong&gt; queued transaction closes -&amp;gt; compute kWh (last &lt;code&gt;sampledValue&lt;/code&gt; minus first &lt;code&gt;sampledValue&lt;/code&gt;) -&amp;gt; price it in INR with GST as applicable (commonly treated as 18% for charging-as-a-service) -&amp;gt; capture the pre-auth or raise a Razorpay invoice -&amp;gt; mark &lt;code&gt;settled&lt;/code&gt;. Pre-auth vs post-paid decides whether we can actually collect: for walk-in users we hold a pre-auth of around INR 500 (our configured hold) at the Started event, so an offline session still has money to capture. Post-paid fleet accounts we invoice monthly, so offline just means late, not unpaid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revenue-leakage cases each get an explicit rule&lt;/strong&gt;, never a silent skip: charger stopped mid-session offline (bill on last good meter reading, flag &lt;code&gt;partial&lt;/code&gt;); missing Updated events (interpolate nothing, use endpoints only); duplicate &lt;code&gt;TransactionEvent&lt;/code&gt; Started after a reboot (same &lt;code&gt;transactionId&lt;/code&gt; wins, reboot copy is dropped); negative or rollback meter readings (reject, route to &lt;code&gt;disputed&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reconciliation ledger&lt;/strong&gt; is a state machine per transaction in the Laravel admin: &lt;code&gt;open -&amp;gt; offline-queued -&amp;gt; replayed -&amp;gt; priced -&amp;gt; settled -&amp;gt; disputed&lt;/code&gt;. Finance sees every offline session and why it was billed what it was.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability for B2B and CEA:&lt;/strong&gt; an immutable, append-only transaction log plus &lt;strong&gt;signed MeterValues&lt;/strong&gt; (2.0.1 &lt;code&gt;signedMeterValue&lt;/code&gt; in each &lt;code&gt;sampledValue&lt;/code&gt;). When a fleet client disputes an invoice, we answer with the cryptographically signed start and stop readings, not a spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  CEA, BIS, and the India compliance picture for secured public chargers
&lt;/h2&gt;

&lt;p&gt;You now have a secure, billable network. The next question every operator asks is which of this is actually required. Be honest about what is law versus what is signal. As of mid-2026, the Ministry of Power's "Guidelines and Standards for Charging Infrastructure" (the Model EV Charging Infrastructure guidelines) and the CEA rules they sit on mostly cover electrical safety, connector standards, and network connectivity, not a hard OCPP security-profile mandate. CEA's cyber-security guidelines and the push toward BIS-tested hardware point at TLS and authenticated firmware as &lt;strong&gt;direction of travel&lt;/strong&gt;, but we have not seen a notified clause that says "run OCPP 2.0.1 Security Profile 3." Treat anyone claiming a strict mandate with suspicion.&lt;/p&gt;

&lt;p&gt;The real near-term forcing function is commercial, not regulatory. &lt;strong&gt;B2B and fleet contracts&lt;/strong&gt; are where mTLS, signed firmware, and audit trails become non-negotiable. A logistics fleet buying charging as a service wants revocation handling and tamper-evident logs written into the SLA, long before a regulator asks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data residency and PII&lt;/strong&gt; is the sharper edge. RFID &lt;code&gt;idToken&lt;/code&gt; values, app identities, and &lt;code&gt;TransactionEvent&lt;/code&gt; records are personal data under the DPDP Act. Know where that data lives, why you hold it, and how to erase it. We cover the mechanics in our &lt;a href="https://dev.to/blog/dpdp-act-compliance-india-2026-website-saas-guide/"&gt;DPDP compliance guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the RFPs we have responded to, security posture is now a tender differentiator: government and large-CPO tenders increasingly list TLS, OCSP revocation, and signed firmware as scored line items. The honest gap: much Indian field hardware still ships with weak or default TLS config, so compliance is as much a fleet-audit exercise (charger by charger) as a backend one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling security onto a live fleet without bricking chargers
&lt;/h2&gt;

&lt;p&gt;Do not flag-day this. Stand up your &lt;strong&gt;plain endpoint&lt;/strong&gt; (&lt;code&gt;wss://&lt;/code&gt; on Security Profile 1) and your &lt;strong&gt;hardened endpoint&lt;/strong&gt; side by side, and migrate chargers in &lt;strong&gt;rings&lt;/strong&gt;, not all at once. Keep a rollback URL live until a given firmware build has proven itself on Profile 2/3 for a couple of weeks. A charger you cannot reach is a charger you cannot fix remotely.&lt;/p&gt;

&lt;p&gt;The order that worked for us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TLS server-auth first&lt;/strong&gt; (Profile 2): CSMS presents a cert, charger validates the chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push client certs&lt;/strong&gt; via &lt;code&gt;SignCertificate&lt;/code&gt; / &lt;code&gt;CertificateSigned&lt;/code&gt;, confirm with &lt;code&gt;GetInstalledCertificateIds&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flip to mTLS&lt;/strong&gt; (Profile 3) per charger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable OCSP revocation checking.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on signed firmware enforcement&lt;/strong&gt; last.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Qualify per model.&lt;/strong&gt; Certify each vendor firmware version against your exact security config in a lab or single-charger canary before it touches a ring. Vendor TLS stacks ship bugs (wrong SNI handling, broken chain validation, clock-skew cert failures) far more often than not. Assume it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get observability up before you touch anything:&lt;/strong&gt; per-charger &lt;code&gt;BootNotification&lt;/code&gt;/&lt;code&gt;Heartbeat&lt;/code&gt; connection state, cert-expiry dashboards (Prometheus plus Grafana works), reconnect-rate alarms, and offline-transaction backlog depth from queued &lt;code&gt;TransactionEvent&lt;/code&gt; messages. A security change that silently kills connectivity should page you in minutes, not surface as a Razorpay reconciliation gap next week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For the CFO:&lt;/strong&gt; in our estimate this is roughly 2 to 4 engineer-weeks of work plus existing infra, not new hardware, the kind of &lt;a href="https://dev.to/services/"&gt;security and backend engineering&lt;/a&gt; we take on. The payoff is B2B fleet contracts that demand it, and not being the operator in the next charger-hack headline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes we hit in production (and the fix)
&lt;/h2&gt;

&lt;p&gt;We learned most of this the hard way. Here is the short list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cert expiry killed a live charger.&lt;/strong&gt; Rotation was manual, someone missed a renewal, and the site dropped off during a busy evening. Now we run scheduled rotation via &lt;code&gt;InstallCertificate&lt;/code&gt; / &lt;code&gt;CertificateSigned&lt;/code&gt; and alert at 30 days before expiry, per charger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reconnect storm after a tower outage.&lt;/strong&gt; When a Jio tower recovered, about a dozen chargers slammed the gateway with simultaneous TLS handshakes and CPU spiked. Fix: jittered reconnect backoff on the charger side plus handshake rate-limiting at the Go gateway, so &lt;code&gt;BootNotification&lt;/code&gt; arrivals spread out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate billing from replayed offline transactions.&lt;/strong&gt; After reconnect, queued &lt;code&gt;TransactionEvent&lt;/code&gt; messages replayed and some settled twice. We now key on &lt;code&gt;transactionId&lt;/code&gt; + &lt;code&gt;seqNo&lt;/code&gt; as an idempotency key and hold a settled-once ledger state, so Razorpay only ever charges the delta once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A charger trusted a bad cert.&lt;/strong&gt; Firmware cert validation was permissive and accepted the wrong CA. We only caught it by testing with a deliberately wrong cert. That test is now part of per-model qualification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline meter drift produced a wrong INR amount.&lt;/strong&gt; We had billed off the charger wall-clock timestamp. Now we bill on metered kWh delta from the first to the last &lt;code&gt;sampledValue&lt;/code&gt; reading in the &lt;code&gt;TransactionEvent&lt;/code&gt; stream, never on clock time.&lt;/p&gt;

&lt;p&gt;2.0.1 security is not a feature you switch on. It is a discipline you run every day: certs, revocation, offline policy, reconciliation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you
&lt;/h2&gt;

&lt;p&gt;None of this is exotic. It is TLS you enforce properly, a small CA you actually run, revocation you actually check, an offline policy you decide on purpose, and a reconciliation ledger that bills each session exactly once. The hard part is not the spec, it is doing all of it every day across real hardware that ships with real bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are securing a live OCPP 2.0.1 network or building a CSMS from scratch and want people who have run this in production rather than read about it, talk to us.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Work with buildbyRaviRai&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions about OCPP 2.0.1 security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need OCPP Security Profile 3 (mTLS), or is Profile 2 (TLS plus Basic auth) enough for a public charging network in India in 2026?
&lt;/h3&gt;

&lt;p&gt;Profile 2 is the honest floor for a public network. It gives you real transport encryption with a single server cert and no per-charger PKI to run day one. Move to Profile 3 (mTLS) when a B2B fleet contract, an aggregator, or a CEA/BIS posture actually demands cryptographic per-station identity, not before. We run Profile 2 as our default and flip specific chargers to Profile 3 when a contract requires it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who issues the TLS certificates for my chargers, and how do I rotate them across a remote fleet without a site visit to each charger?
&lt;/h3&gt;

&lt;p&gt;You do, effectively. Once you turn on Profile 2 or 3 you run a small CA: a CPO root, a sub-CA for charger client certs, and a sub-CA anchoring the CSMS server cert. Rotation rides the live OCPP WebSocket via &lt;code&gt;SignCertificate&lt;/code&gt;, &lt;code&gt;CertificateSigned&lt;/code&gt;, and &lt;code&gt;InstallCertificate&lt;/code&gt;, so there is no truck roll. We alarm any charger whose cert is inside 30 days of expiry and reissue ahead of it, because an expired cert on a remote charger is a self-inflicted outage.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does a charger authorize a user and start a session when the 4G/mobile network drops mid-site, and is it safe on a public charger?
&lt;/h3&gt;

&lt;p&gt;It falls back to two local mechanisms: the operator-pushed Local Authorization List and the Authorization Cache of previously approved &lt;code&gt;idTokens&lt;/code&gt;. With &lt;code&gt;LocalAuthorizeOffline&lt;/code&gt; and &lt;code&gt;LocalPreAuthorize&lt;/code&gt; on, a known token starts energy immediately without the CSMS. Safety is a policy call: &lt;code&gt;OfflineTxForUnknownIdEnabled&lt;/code&gt; controls whether an unknown token can start offline, and on an open public charger that is a deliberate revenue-versus-fraud trade-off you set per site and write down.&lt;/p&gt;

&lt;h3&gt;
  
  
  If a charging session runs entirely offline, how do I bill it through Razorpay when connectivity returns without double-charging the user or losing the revenue?
&lt;/h3&gt;

&lt;p&gt;The charger stores the transaction locally and replays queued &lt;code&gt;TransactionEvent&lt;/code&gt; messages when the link returns. We dedupe on the charger's &lt;code&gt;transactionId&lt;/code&gt; plus &lt;code&gt;seqNo&lt;/code&gt; with a unique constraint in Postgres, so a retried Ended event cannot create a second charge. For walk-in users we hold a pre-auth of around INR 500 at session start, so there is money to capture even if the session ran dark; fleet accounts get invoiced monthly instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is ISO 15118 Plug and Charge actually usable in India yet, and do I have to stand up a full PKI to support it?
&lt;/h3&gt;

&lt;p&gt;Not really, not yet. The V2G root and contract-certificate ecosystem in India is still thin, so Plug and Charge is design-for-later, not a 2026 requirement. Build the hooks in your data model and keep V2G certs as a separate certificate family so you are not conflating them later. Do not stand up a full V2G PKI now to chase a feature the market cannot use.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does the charger check whether a certificate has been revoked (OCSP), and what should it do when it cannot reach the OCSP responder?
&lt;/h3&gt;

&lt;p&gt;In 2.0.1 the charger sends &lt;code&gt;GetCertificateStatus&lt;/code&gt; with the OCSP request and the CSMS relays it to the responder, so the charger needs no direct route out. We staple the signed answer into the TLS handshake to save a round trip on flaky links, and we cache and refresh staples before they go stale. When nothing can reach a responder we soft-fail with a short grace window on unattended public sites, because hard-fail bricks a whole site when the responder blips, and we alarm on stale staples.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do CEA and BIS actually require for security on public EV chargers in 2026, and will regulators or fleet clients force me to mTLS first?
&lt;/h3&gt;

&lt;p&gt;As of mid-2026 we have not seen a notified clause mandating a specific OCPP security profile; the Ministry of Power's "Guidelines and Standards for Charging Infrastructure" mostly cover electrical safety, connectors, and connectivity, with TLS and signed firmware reading as direction of travel. The real forcing function is commercial: B2B and fleet contracts write mTLS, revocation, and tamper-evident logs into the SLA long before a regulator does. Plan your PKI around the next fleet tender, not around a mandate that is not on paper yet.&lt;/p&gt;

</description>
      <category>ocpp201security</category>
      <category>ocppsecurityprofiles</category>
      <category>ocppmtlsmutualtls</category>
      <category>ocpp201tlscertificatemanagemen</category>
    </item>
    <item>
      <title>Tally Integration With Your Website or Custom Software (India, 2026): How It Actually Works, What It Costs, and When to Build vs Buy</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Sun, 12 Jul 2026 22:41:22 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/tally-integration-with-your-website-or-custom-software-india-2026-how-it-actually-works-what-5dc3</link>
      <guid>https://dev.to/buildbyravirai/tally-integration-with-your-website-or-custom-software-india-2026-how-it-actually-works-what-5dc3</guid>
      <description>&lt;p&gt;If you run a business in India, there is a good chance your sales happen in one system and your accounting happens in &lt;strong&gt;TallyPrime&lt;/strong&gt;, and the two have never spoken to each other. Someone bridges that gap by hand, copying orders into Tally one voucher at a time. It works until volume grows. Then it quietly costs you hours and introduces errors nobody spots until GST filing.&lt;/p&gt;

&lt;p&gt;This guide is about closing that gap properly: connecting your website, e-commerce store, or custom software to TallyPrime so orders, invoices, ledgers, and stock flow in without anyone re-typing them. We have built these integrations for D2C stores, B2B distributors, and service firms running a CRM next to Tally, so this is written from what actually breaks in production, not a vendor brochure.&lt;/p&gt;

&lt;p&gt;By the end you will understand how Tally really connects to another app, the three integration methods and when each fits, whether to buy a connector or build custom middleware, what the whole thing costs in 2026, and how to scope the project before you hire anyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this comes up: your sales live in one app, your books live in Tally
&lt;/h2&gt;

&lt;p&gt;Every Indian SMB we work with hits the same wall. The sales happen in one place, and the accounting happens in &lt;strong&gt;TallyPrime&lt;/strong&gt;, and nothing connects the two.&lt;/p&gt;

&lt;p&gt;So someone pays the tax. Usually the accountant. They open the website admin (or the store dashboard, or the CRM), read off one order, then retype it into Tally as a sales voucher. Party ledger, item, quantity, GST, done. Then the next order. Then dozens more. We call it the &lt;strong&gt;re-keying tax&lt;/strong&gt;, and it costs real hours plus the typos nobody catches until reconciliation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who feels it worst:&lt;/strong&gt; D2C and e-commerce stores pushing dozens of orders a day, B2B distributors booking stock and party ledgers, and service firms running a CRM next to Tally for invoices and receipts.&lt;/p&gt;

&lt;p&gt;When people say '&lt;strong&gt;Tally integration with a website&lt;/strong&gt;', this is what they actually mean: your own app pushing its sales, invoices, ledgers, and stock straight into Tally so nobody re-keys anything.&lt;/p&gt;

&lt;p&gt;One clear line first. This is &lt;strong&gt;not GST e-invoicing&lt;/strong&gt;. E-invoicing means generating an IRN on the government IRP portal (we cover &lt;a href="https://dev.to/blog/gst-e-invoicing-irp-irn-developer-guide-india-2026/"&gt;GST e-invoicing and IRN generation&lt;/a&gt; separately). This is booking &lt;em&gt;your own&lt;/em&gt; vouchers into &lt;em&gt;your own&lt;/em&gt; Tally.&lt;/p&gt;

&lt;p&gt;So how does your app actually push data into Tally? It starts with a fact most vendors skip over.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Tally actually connects to another app (the part nobody explains plainly)
&lt;/h2&gt;

&lt;p&gt;Here is the thing most vendors gloss over: &lt;strong&gt;TallyPrime is its own little web server.&lt;/strong&gt; Go to &lt;strong&gt;F1 (Help) &amp;gt; Settings &amp;gt; Connectivity &amp;gt; Client/Server Configuration&lt;/strong&gt;, set TallyPrime to act as &lt;strong&gt;Server&lt;/strong&gt;, and enable the gateway (the &lt;a href="https://help.tallysolutions.com/" rel="noopener noreferrer"&gt;TallyPrime help documentation&lt;/a&gt; lists the exact toggle labels for your release). Tally then starts listening for HTTP requests on its gateway port, 9000 by default. Once that is on, any other program on the same machine or network can talk to it. That is the whole foundation. No magic, no API keys, just a local server sitting there waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The four real doors in:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;XML over HTTP&lt;/strong&gt;, the workhorse. You send a request to &lt;code&gt;http://&amp;lt;tally-ip&amp;gt;:9000&lt;/code&gt; and Tally answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TDL (Tally Definition Language)&lt;/strong&gt;, Tally's own scripting layer for custom reports, fields, and behaviour inside Tally itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ODBC&lt;/strong&gt;, read-heavy, good for pulling data into Excel or Power BI, weaker for writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prebuilt connectors&lt;/strong&gt;, tools that wrap the above so you don't write raw XML.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Import vs export, in plain words.&lt;/strong&gt; To push data in (create sales vouchers, customer ledgers, stock items), you &lt;strong&gt;POST an XML request&lt;/strong&gt; with &lt;code&gt;&amp;lt;REQUEST&amp;gt;&lt;/code&gt; type &lt;code&gt;Import Data&lt;/code&gt;. To pull data out (a sales register, outstanding receivables, a stock summary), you send an &lt;code&gt;Export Data&lt;/code&gt; request and Tally streams the report back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The request envelope.&lt;/strong&gt; Every message is an &lt;code&gt;&amp;lt;ENVELOPE&amp;gt;&lt;/code&gt; with two parts: a &lt;code&gt;&amp;lt;HEADER&amp;gt;&lt;/code&gt; (what you want, e.g. &lt;code&gt;Import Data&lt;/code&gt;) and a &lt;code&gt;&amp;lt;BODY&amp;gt;&lt;/code&gt; that holds one or more &lt;code&gt;&amp;lt;TALLYMESSAGE&amp;gt;&lt;/code&gt; blocks (the actual voucher or master). Inside the header you must name the &lt;strong&gt;company&lt;/strong&gt; and the &lt;strong&gt;financial year&lt;/strong&gt; exactly as they appear in Tally. Get the company name off by one space, or point at the wrong FY, and Tally silently rejects or misfiles the voucher. This is one of the most common causes of failed imports we see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The constraint you never escape.&lt;/strong&gt; Tally has to be &lt;strong&gt;open, unlocked, and network-reachable&lt;/strong&gt; at the moment of every request. Close Tally, lock the screen, or lose the LAN, and the integration stops. There is no background daemon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-prem vs cloud Tally.&lt;/strong&gt; If Tally runs on a desktop in the office, your integration is only alive during office hours. If you host Tally on a &lt;strong&gt;cloud VM&lt;/strong&gt; (AWS, or a managed Tally-on-cloud host), it can run 24x7 and your website can reach it any time. That one decision shapes the entire architecture, and we come back to it in detail below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The integration methods compared: XML API, TDL, and ODBC
&lt;/h2&gt;

&lt;p&gt;When your website, store, or CRM has to talk to &lt;strong&gt;TallyPrime&lt;/strong&gt;, you have three real native options. Not ten. Three. Prebuilt connectors are a fourth path, but they just wrap these three, and we cover them under build vs buy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XML over HTTP API.&lt;/strong&gt; This is the default path and the one we reach for on most jobs. Tally accepts XML requests on the port from the last section and returns XML. TallyPrime added JSON import in Release 7.0, but for the API path we default to XML, which every TallyPrime build accepts over the HTTP gateway. It is language-agnostic, so your middleware can be written in &lt;strong&gt;Node, PHP, or Python&lt;/strong&gt; and it does not care. You POST a request that says 'create this sales voucher' or 'give me this ledger balance', Tally does it, you read the response. This is how app data actually gets pushed into the books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TDL (Tally Definition Language).&lt;/strong&gt; This customises Tally itself: new fields, new reports, extra buttons, changed voucher layouts. It is genuinely powerful, but the code lives inside Tally and runs on Tally's terms (the language is documented by &lt;a href="https://tallysolutions.com/" rel="noopener noreferrer"&gt;Tally Solutions&lt;/a&gt;). You need an actual &lt;strong&gt;Tally/TDL developer&lt;/strong&gt;, not a web dev. Use it when the requirement is inside Tally (a custom invoice format, a new master field), not when the requirement is 'sync my website orders'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ODBC.&lt;/strong&gt; Read-oriented reporting into &lt;strong&gt;Excel, Power BI, or Google Sheets&lt;/strong&gt;. Good for pulling numbers out. Clunky and weak for writing data in, and the connection is fiddly to keep alive. Treat it as a reporting tap, not an integration.&lt;/p&gt;

&lt;p&gt;Here is the same comparison in plain words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;XML over HTTP&lt;/strong&gt; is genuinely good at pushing and pulling vouchers, ledgers, and stock from any app. Where it falls down: Tally must be open, and XML is verbose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TDL&lt;/strong&gt; is genuinely good at custom fields, reports, and invoice formats. Where it falls down: it needs a Tally developer and lives inside Tally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ODBC&lt;/strong&gt; is genuinely good at reading into Excel or BI tools. Where it falls down: writing data is unstable and slow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The practical answer.&lt;/strong&gt; For website, CRM, or e-commerce work, it is almost always &lt;strong&gt;XML over HTTP driven by a middleware service you control&lt;/strong&gt;. The middleware queues orders, maps GST/HSN and ledgers, retries when Tally is closed, and logs every push. TDL and ODBC play supporting roles around it, never the lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect Tally to a website, e-commerce store, or CRM: the real scenarios
&lt;/h2&gt;

&lt;p&gt;Every integration comes down to the same move: something happens in your app, and a &lt;strong&gt;voucher&lt;/strong&gt; or &lt;strong&gt;ledger&lt;/strong&gt; gets created in TallyPrime over its XML port. What changes is the source and how often you push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website or lead form to Tally.&lt;/strong&gt; A quote request or a paid order hits your site. The middleware first checks whether a &lt;strong&gt;party ledger&lt;/strong&gt; exists (matched on name or GSTIN), creates it under Sundry Debtors if not, then posts a &lt;strong&gt;Sales voucher&lt;/strong&gt; or a &lt;strong&gt;Receipt voucher&lt;/strong&gt; for a prepaid order. The per-order data entry disappears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shopify, WooCommerce, or a custom store to Tally.&lt;/strong&gt; Here each order carries line items, so you map every SKU to a Tally &lt;strong&gt;stock item&lt;/strong&gt;, post the order as a Sales voucher with &lt;strong&gt;HSN codes&lt;/strong&gt; and split &lt;strong&gt;CGST/SGST/IGST&lt;/strong&gt; by the buyer's state, and let Tally reduce stock. Get the tax ledger mapping right once and reconciliation stops being a monthly fire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CRM to Tally (our &lt;a href="https://dev.to/products/crm-multivendor/"&gt;MultiVendor CRM&lt;/a&gt; as the worked example).&lt;/strong&gt; When a deal moves to Won, we push the CRM customer as a ledger and the CRM invoice as a Sales voucher, keeping the CRM invoice number as the voucher reference so both systems agree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-way vs two-way.&lt;/strong&gt; One-way push (app to Tally) covers most Indian SMBs, because accounts is the system of record and nobody wants website prices editable from Tally. Two-way sync (pulling outstanding balances or payment status back) costs more to build and test, so buy it only when your sales team genuinely needs live balances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Near-real-time vs batch.&lt;/strong&gt; Our rule of thumb: under roughly 50 to 100 orders a day, fire on each order. Above that, or when Tally sits on one office PC that isn't always awake, we run a &lt;strong&gt;scheduled batch&lt;/strong&gt; every 15 to 30 minutes. Batch is also far kinder to debug when a voucher fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build vs buy: off-the-shelf connector or custom middleware
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What ready-made connectors do well.&lt;/strong&gt; If your setup is a standard store feeding a standard books file, a packaged connector is the fastest way to stop double-entering vouchers. Off-the-shelf WooCommerce-to-Tally, Shopify-to-Tally, and Zoho-to-Tally connectors handle the common path well: a paid order becomes a sales voucher, the customer becomes a ledger, stock items map to Tally stock items, and CGST/SGST/IGST post to the right ledgers. For a single store with clean product data, expect setup in days, not weeks. This is the right call more often than founders think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where connectors break down.&lt;/strong&gt; The trouble starts when your flow stops being generic. Custom fields (a dispatch code, a broker name, a project tag) usually have nowhere to land. Non-standard voucher structures, like a sales voucher that also books TCS or a cost centre split, either get dropped or forced into the wrong ledger. And most connectors are one-source-to-one-Tally. The moment you have a website plus an offline POS plus your own CRM (the kind of &lt;a href="https://dev.to/blog/custom-crm-development-india-2026-cost-vs-salesforce-hubspot/"&gt;custom CRM development&lt;/a&gt; that carries its own fields) all needing to hit the same company file, an off-the-shelf tool cannot arbitrate between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What custom middleware buys you.&lt;/strong&gt; A middleware layer (a small service that sits between your apps and TallyPrime's XML port) gives you control that a licence screen never will: your own field mapping, &lt;strong&gt;idempotency keys&lt;/strong&gt; so a retried webhook does not create a duplicate voucher, retry-with-backoff when Tally is closed, a full audit log of every payload sent, and deliberate handling of GST edge cases (reverse charge, exempt vs nil-rated, HSN mismatches). When something fails at 11pm, you can read the log instead of guessing. This is the sort of &lt;a href="https://dev.to/services/"&gt;custom software development services&lt;/a&gt; work we take on most weeks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An honest framework.&lt;/strong&gt; Buy if your flow is standard and single-source. Build if it is bespoke, has custom fields, or pulls from more than one system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The money trade-off.&lt;/strong&gt; Most connectors run on a recurring per-year licence, roughly &lt;strong&gt;₹8,000 to ₹30,000 per year&lt;/strong&gt;. A custom build is a one-time cost you own outright, commonly &lt;strong&gt;₹40,000&lt;/strong&gt; for a simple one-way sync and up to &lt;strong&gt;₹8,00,000+&lt;/strong&gt; for a multi-source setup, depending on voucher complexity. Over three years, bespoke and multi-source setups usually favour building. The cost section below breaks the tiers down properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  A JSON-to-Tally-XML middleware: how we architect it in Node or PHP
&lt;/h2&gt;

&lt;p&gt;The core pattern is simple once you see it. Your website, store, or CRM does not talk to Tally directly. It emits a plain &lt;strong&gt;JSON event&lt;/strong&gt; ('order paid', 'invoice raised') to a small &lt;strong&gt;middleware service&lt;/strong&gt;. That service maps the JSON to a &lt;strong&gt;Tally XML envelope&lt;/strong&gt; and POSTs it to &lt;code&gt;http://&amp;lt;tally-host&amp;gt;:9000&lt;/code&gt;. Tally receives the voucher and replies with its own XML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The service structure&lt;/strong&gt; is the same whether we build it in Node (Express or Fastify) or PHP (Laravel), and it follows the same &lt;a href="https://dev.to/blog/rest-api-design-best-practices-india-2026/"&gt;REST API design best practices&lt;/a&gt; we use on any integration layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Receive&lt;/strong&gt; the event on a webhook route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; it: does the ledger exist, is the GST/HSN present, is the amount sane?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transform&lt;/strong&gt; the JSON into the exact &lt;code&gt;&amp;lt;TALLYMESSAGE&amp;gt;&lt;/code&gt; block the voucher type needs, with &lt;code&gt;LEDGERNAME&lt;/code&gt;, &lt;code&gt;AMOUNT&lt;/code&gt;, and the party details wrapped in &lt;code&gt;ENVELOPE&lt;/code&gt; &amp;gt; &lt;code&gt;BODY&lt;/code&gt; &amp;gt; &lt;code&gt;IMPORTDATA&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send&lt;/strong&gt; it to port 9000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parse&lt;/strong&gt; the response for &lt;code&gt;&amp;lt;LINEERROR&amp;gt;&lt;/code&gt; or a created/altered count.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;A queue and retry layer is not optional.&lt;/strong&gt; Desktop Tally gets closed at 7pm, or someone is mid-entry and it is busy. Without a queue the POST fails and the sale vanishes from the books silently. We push every event into &lt;strong&gt;Redis (BullMQ)&lt;/strong&gt; or a database-backed queue. If Tally is unreachable, the job waits and re-fires on a backoff. Nothing is lost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency keys stop duplicate ledgers and vouchers.&lt;/strong&gt; We tie a key to your &lt;strong&gt;order ID or invoice number&lt;/strong&gt;. Before posting, the middleware checks whether that key already succeeded. So a retry, a double webhook, or a manual replay never creates two sales vouchers for the same order. This one control prevents the most common Tally-integration mess, and we come back to it below because it is the number one thing that goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log everything verbatim.&lt;/strong&gt; We store the outbound XML and Tally's raw reply for every request. When the accountant asks why a voucher looks off, you open the log, not a debugger. We typically keep at least 90 days of logs, then prune older ones, since the payloads carry customer and GSTIN data you should not hoard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it runs matters.&lt;/strong&gt; For desktop Tally, the middleware sits on the &lt;strong&gt;same LAN&lt;/strong&gt;, often the same machine or a small always-on PC. For cloud-hosted Tally, we run it on a &lt;strong&gt;small VPS&lt;/strong&gt; (roughly ₹500 to ₹2,000 per month) that reaches Tally over a &lt;strong&gt;private tunnel&lt;/strong&gt; (&lt;a href="https://www.wireguard.com/" rel="noopener noreferrer"&gt;WireGuard&lt;/a&gt; or Tailscale), never an open public port.&lt;/p&gt;

&lt;h2&gt;
  
  
  The things that actually break (and how we handle each one)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Duplicate vouchers from retries with no idempotency.&lt;/strong&gt; This is the number one thing that goes wrong. Your app posts a sale, Tally is slow, the request times out, your app retries, and now you have the same invoice twice in the books. The fix is the idempotency key from the last section: we store every posted voucher's key plus Tally's returned voucher ID in a small ledger table, and we check that table before any post and before any retry. No key means no duplicate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ledger master must exist before the voucher.&lt;/strong&gt; Tally rejects a sales voucher if the party ledger is not already there. So our middleware checks for the party and tax ledgers first, auto-creates the ledger master (with the right group, GSTIN, and state) via XML, then posts the voucher in the same run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GST, HSN, and tax-ledger mapping mismatches.&lt;/strong&gt; Your app might call it 'GST 18%' while your accountant's Tally has a specific 'Output CGST 9% / SGST 9%' setup with HSN codes on the stock items. We build a mapping table, agreed with your accountant, that translates your app's tax fields to the exact ledger names and &lt;a href="https://www.gst.gov.in/" rel="noopener noreferrer"&gt;HSN codes&lt;/a&gt; in their company. Get this wrong and every GSTR-1 reconciliation becomes manual.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Company name or financial year mismatch.&lt;/strong&gt; As noted earlier, Tally silently rejects the whole request if the SVCURRENTCOMPANY or the date falls outside the open financial year. We pin the exact company name and validate the voucher date against the active FY before sending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tally not running, machine asleep, or office internet down.&lt;/strong&gt; On-prem Tally is offline more than you think. The durable queue in front (Redis or a simple DB table) means posts pile up safely and drain automatically when Tally is back. Nothing is lost, nothing is posted twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voucher numbering clashes.&lt;/strong&gt; If Tally auto-numbers and your app also forces a number, you get gaps or rejections. We pick one owner of the number, usually Tally, and store the returned number back against your order.&lt;/p&gt;

&lt;p&gt;Typical build for this hardening: &lt;strong&gt;₹40,000 to ₹90,000&lt;/strong&gt;, which sits inside the simple one-way tier below.&lt;/p&gt;

&lt;h2&gt;
  
  
  On-prem vs cloud Tally, and reaching it safely over the internet
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Desktop Tally on an office PC.&lt;/strong&gt; This is the most common setup we see. TallyPrime is installed on one machine in the accounts room, and the XML server listens on port 9000 on that machine's local network only. Your middleware has to reach that machine. If the middleware also runs inside the office (same LAN), it just points at the PC's local IP, for example &lt;code&gt;http://192.168.1.12:9000&lt;/code&gt;. If your website or web app lives on a hosted server, it cannot see that local IP at all. You need a secure tunnel between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tally on a cloud VM or a Tally-on-cloud provider.&lt;/strong&gt; Move Tally onto a Windows VM (AWS EC2, Azure, or a managed Tally-on-cloud host) and your hosted app can reach it reliably, because both sit on the internet. Expect roughly &lt;strong&gt;₹800 to ₹2,500 per user per month&lt;/strong&gt; for a managed Tally-on-cloud seat. This is what we recommend when uptime matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting safely.&lt;/strong&gt; Your options: a &lt;strong&gt;static IP plus firewall whitelist&lt;/strong&gt;, a &lt;strong&gt;site-to-site or client VPN&lt;/strong&gt; (WireGuard, OpenVPN), or a &lt;strong&gt;reverse tunnel&lt;/strong&gt; (&lt;a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" rel="noopener noreferrer"&gt;Cloudflare Tunnel&lt;/a&gt;, ngrok, Tailscale) from the Tally PC out to your middleware. VPN and reverse tunnel are safest because nothing is opened inbound on the office router.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security rule we never break:&lt;/strong&gt; do not port-forward or expose raw port 9000 to the public internet. The gateway has no built-in authentication. Anyone who finds it can read and post vouchers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After go-live&lt;/strong&gt;, keep the Tally licence activated on that exact machine, leave Gateway of Tally on port 9000, and confirm whether you are in single-user or multi-user (Gold) mode. Single-user locks the company while a report runs, which will stall your sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs and how long it takes (India, 2026)
&lt;/h2&gt;

&lt;p&gt;Pricing here is indicative for 2026 and reflects our own rate card; confirm current quotes before you budget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Off-the-shelf connectors.&lt;/strong&gt; If your setup is standard (WooCommerce or Shopify orders flowing into TallyPrime as sales vouchers), a ready-made connector is the cheapest start. Expect roughly &lt;strong&gt;₹8,000 to ₹30,000 per year&lt;/strong&gt; for a licensed plug-in or SaaS bridge, usually billed annually per Tally company or per site. Off-the-shelf WooCommerce-to-Tally, Shopify-to-Tally, and Zoho-to-Tally connectors sit in this band. The catch: they only map the fields the vendor decided to support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom middleware.&lt;/strong&gt; The moment your data does not fit a template, you are building. Rough ranges we quote:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple one-way sync&lt;/strong&gt; (website orders into Tally, one company, standard GST): &lt;strong&gt;₹40,000 to ₹1,00,000&lt;/strong&gt;, delivered in &lt;strong&gt;1 to 2 weeks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mapped two-way sync&lt;/strong&gt; (stock and ledger balances flow back to the site, custom fields, HSN mapping): &lt;strong&gt;₹1,25,000 to ₹3,50,000&lt;/strong&gt;, &lt;strong&gt;3 to 6 weeks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-source&lt;/strong&gt; (website plus CRM plus marketplace into one or more Tally companies): &lt;strong&gt;₹3,50,000 to ₹8,00,000+&lt;/strong&gt;, &lt;strong&gt;6 to 12 weeks&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ongoing costs people forget.&lt;/strong&gt; Middleware has to run somewhere. A small &lt;strong&gt;VPS to host the sync service&lt;/strong&gt; is around &lt;strong&gt;₹500 to ₹2,000 per month&lt;/strong&gt;. Budget an &lt;strong&gt;annual maintenance retainer&lt;/strong&gt; (we typically charge &lt;strong&gt;15 to 20% of build cost&lt;/strong&gt;) because every &lt;strong&gt;TallyPrime version upgrade&lt;/strong&gt; can change XML behaviour and quietly break your import. On-prem Tally also means the machine and the sync service both have to stay awake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What drives the price up.&lt;/strong&gt; Custom fields, two-way writes, multiple source apps, and &lt;strong&gt;GST edge cases&lt;/strong&gt; (inclusive vs exclusive tax, mixed HSN slabs, place-of-supply logic, credit notes). Each one adds mapping and testing time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cheapest sane path.&lt;/strong&gt; A &lt;strong&gt;small store&lt;/strong&gt; should buy a connector, live with its limits, and only build later. A &lt;strong&gt;growing B2B distributor&lt;/strong&gt; with pricing tiers, multiple ledgers, and stock that must stay accurate across channels should skip the annual connector rental and commission &lt;strong&gt;custom middleware&lt;/strong&gt; once, because the duplicate-ledger and reconciliation cleanup will cost more than the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to scope your Tally integration before you hire anyone
&lt;/h2&gt;

&lt;p&gt;The projects that go badly are the ones nobody scoped. Spend an afternoon on this and you will cut both cost and rework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write down exactly what data moves.&lt;/strong&gt; One line per object: 'sales orders from Shopify create sales vouchers in Tally', 'customer master syncs both ways'. Note direction (one-way or two-way) and volume per day. This one page is what a developer actually quotes against.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decide sync frequency and the tie-breaker.&lt;/strong&gt; Real-time on order, or a batch every 15 minutes? More importantly, when the website says one price and Tally says another, who wins? Pick a source of truth per field before a line of code is written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirm on-prem vs cloud Tally.&lt;/strong&gt; Is TallyPrime on a shop-floor PC, a VPS, or something like Tally on AWS? The developer needs to reach port 9000, usually via a static IP, a VPN, or an agent running next to Tally. Sort this early; it blocks everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get a sample voucher and ledger structure from your accountant up front.&lt;/strong&gt; Real ledger names, GST/HSN codes, voucher types, and one exported XML voucher. Guessing the ledger tree is where duplicate ledgers get born.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put the boring parts in the contract.&lt;/strong&gt; Idempotency (no double-posting on retry), retry logic, a log of every push, and email alerts on failure. Ask for these by name, not 'good error handling'.&lt;/p&gt;

&lt;p&gt;A quick &lt;a href="https://dev.to/website-cost-calculator/"&gt;website cost calculator&lt;/a&gt; helps you sanity-check the build budget while you scope. Off-the-shelf connectors run roughly ₹8,000 to ₹30,000 per year; custom middleware more. Do this homework and any developer you hire starts from a data map instead of a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions about Tally integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Tally have an API, and can it connect to a website or custom software?
&lt;/h3&gt;

&lt;p&gt;Yes, though not a REST API in the modern sense. TallyPrime runs a local HTTP-XML server on port 9000 once you enable Client/Server mode and set it to act as a server. Any app, in any language, can POST XML (newer builds also accept JSON) to create or read vouchers, ledgers, and stock. Your website or custom software connects through a middleware service that speaks that XML, not through a hosted cloud endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does TallyPrime have to be open and running for the integration to work?
&lt;/h3&gt;

&lt;p&gt;Yes. Tally must be open, unlocked, and network-reachable at the exact moment of every request, because there is no background service that runs when the app is closed. This is why on-prem Tally is effectively an office-hours integration. The standard fix is a durable queue in front of Tally, so events wait safely and drain automatically once Tally is back, and to host Tally on a cloud VM if you need 24x7 uptime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I integrate cloud-hosted Tally with my web app, or does it only work on the office PC?
&lt;/h3&gt;

&lt;p&gt;Both work, and the choice shapes the whole architecture. Desktop Tally on an office PC only listens on the local network, so a hosted web app has to reach it through a VPN or a reverse tunnel, never an exposed public port 9000. Cloud-hosted Tally on a Windows VM or a Tally-on-cloud provider sits on the internet alongside your app, so it is reachable any time and is what we recommend when uptime matters, at roughly ₹800 to ₹2,500 per user per month.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does Tally integration cost in India in 2026, and how long does it take?
&lt;/h3&gt;

&lt;p&gt;A ready-made connector for a standard store runs about ₹8,000 to ₹30,000 per year and sets up in days. Custom middleware is a one-time build: ₹40,000 to ₹1,00,000 for a simple one-way sync in 1 to 2 weeks, rising to ₹3,50,000 to ₹8,00,000+ over 6 to 12 weeks for multi-source setups. Add a small VPS (around ₹500 to ₹2,000 per month) and a maintenance retainer, because Tally version upgrades can change XML behaviour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I buy a ready-made Tally connector or get custom middleware built?
&lt;/h3&gt;

&lt;p&gt;Buy if your flow is standard and single-source: one clean store feeding one company file with normal GST. Build if it is bespoke, has custom fields, non-standard vouchers (TCS, cost centres), or pulls from more than one system that all hit the same Tally company. Over three years, multi-source and bespoke setups almost always favour building, because the reconciliation cleanup a limited connector leaves behind costs more than the one-time build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will syncing orders into Tally create duplicate vouchers, and how is that prevented?
&lt;/h3&gt;

&lt;p&gt;It can, and it is the single most common failure. A slow response, a timeout, a retried webhook, or a manual replay can post the same order twice. The prevention is idempotency keys: tie a stable key to your order ID or invoice number, store every posted voucher's key alongside Tally's returned voucher ID, and check that table before every post and every retry. No matching key, no duplicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest bottom line
&lt;/h2&gt;

&lt;p&gt;Tally integration is not exotic, but it is unforgiving. The connection itself is a few lines of XML; the reason projects go sideways is everything around it, the queue, the idempotency, the exact ledger and GST mapping, and Tally simply being closed at the wrong moment. Get those right and the re-keying tax disappears for good. Get them wrong and you trade manual entry for duplicate-voucher cleanup, which is worse.&lt;/p&gt;

&lt;p&gt;If you want a second opinion on whether to buy a connector or build, or you already know you need custom middleware done properly, that is the kind of work we do. Send us a sample voucher and a line on what data moves, and we will tell you honestly which path fits. Start at &lt;a href="https://dev.to/contact/"&gt;buildbyRaviRai&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you already know you need Tally wired into your website, store, or CRM the right way, that is exactly what we build: idempotent middleware, clean GST and ledger mapping, a durable queue, and a full audit log of every push. Send us a sample voucher and one line on what data moves, and we will tell you honestly whether to buy a connector or build.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Talk to us about Tally integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tallyintegrationwithwebsite</category>
      <category>tallyapiintegration</category>
      <category>integratetallywithcrm</category>
      <category>connecttallytowebapplication</category>
    </item>
    <item>
      <title>How to Actually Accept Payments in India in 2026: UPI, Cards, Autopay, and What Each One Really Costs</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Sat, 11 Jul 2026 04:56:24 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/how-to-actually-accept-payments-in-india-in-2026-upi-cards-autopay-and-what-each-one-really-2di8</link>
      <guid>https://dev.to/buildbyravirai/how-to-actually-accept-payments-in-india-in-2026-upi-cards-autopay-and-what-each-one-really-2di8</guid>
      <description>&lt;p&gt;Sooner or later, every business that sells anything online has to answer a boring but expensive question: how do we actually take the money? In India, that question has a stranger answer than almost anywhere else. We have a national instant-payment rail that is free to accept, a regulator that rewrote the rulebook for payment companies in 2025, recurring payments that do not work the way a founder coming from Stripe expects, and card rules that forbid you from storing a card number at all. Get the setup right and payments fade into the background. Get it wrong and you leak money on fees, fail half your subscription renewals, or sit on a compliance problem you did not know you had.&lt;/p&gt;

&lt;p&gt;This is the practical version, written for founders who want to understand what they are paying for and for developers who have to wire it up. We will cover the options and what each one really costs, the difference between a payment gateway and an aggregator, how UPI and cards and autopay actually behave, how to collect from customers abroad, and a checklist to go live. One honest caveat first: payment rules in India change often, and this is practical guidance, not legal or financial advice. Where a number is likely to move, we say so. Most of this comes from wiring payments into client products in production rather than from reading circulars, though we have read our share of those too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two things you are actually choosing
&lt;/h2&gt;

&lt;p&gt;It helps to separate two decisions that usually get muddled together. The first is who holds and settles your money, which is your payment aggregator, the company whose account collects from your customer and then pays out to your bank. The second is which payment methods you accept: UPI, credit and debit cards, netbanking, wallets, and so on. You pick one aggregator and, through it, switch on the methods you want. Almost every business gets both by signing up with a single provider like Razorpay, Cashfree, or PayU, so in practice the real decision is which aggregator, and the methods are just toggles you turn on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gateway or aggregator: the distinction that trips people up
&lt;/h2&gt;

&lt;p&gt;People use 'payment gateway' as a catch-all, but the RBI draws a sharp line between two things. A &lt;strong&gt;payment gateway&lt;/strong&gt; is pure technology: it routes and processes a transaction without ever touching the funds. A &lt;strong&gt;payment aggregator&lt;/strong&gt; actually collects your customers' money into its own account and then settles it to you, which means it handles the float. Because it holds money, an aggregator has to be authorised by the RBI. A pure gateway does not.&lt;/p&gt;

&lt;p&gt;As of 2026 the governing rulebook is the &lt;strong&gt;RBI (Regulation of Payment Aggregators) Directions, 2025&lt;/strong&gt;, notified on 15 September 2025, which replaced the older 2020 guidelines and folded online, offline, and cross-border aggregators into one framework. The part that matters to you as a merchant is simple: an authorised aggregator must keep your collected funds in a segregated &lt;strong&gt;escrow account&lt;/strong&gt; at a scheduled commercial bank, and can use that account only for settling payments. That escrow rule is the safeguard that stops a payment company from quietly spending your float. The good news is you do not need your own aggregator licence. That path involves a net worth of ₹15 crore rising to ₹25 crore and a serious compliance burden. You just need to pick an aggregator that already holds one.&lt;/p&gt;

&lt;h2&gt;
  
  
  UPI: the rail that changed everything
&lt;/h2&gt;

&lt;p&gt;If you sell to Indian consumers, UPI is not optional. It is how most people pay, and for merchants the headline is remarkable: accepting a UPI payment is &lt;strong&gt;free&lt;/strong&gt;. There is no merchant discount rate on UPI person-to-merchant payments, which we get into properly below.&lt;/p&gt;

&lt;p&gt;Mechanically, you collect over UPI in a few ways, and your aggregator handles most of the plumbing. There is the QR code, static or dynamic, that a customer scans. There is the intent flow, where tapping a button on your checkout opens the customer's UPI app with the amount already filled in. And there are UPI deep links for sharing a payable link over WhatsApp or SMS. One change worth knowing if you build your own checkout: the older payer-facing 'collect' request, where the merchant pushes a request that pops up in the customer's app, is being phased out for most cases from early 2026 in favour of the intent and QR flows, which are faster and harder to abuse.&lt;/p&gt;

&lt;p&gt;On limits: the standard UPI cap is &lt;strong&gt;₹1 lakh per transaction&lt;/strong&gt; for most payments. Certain verified merchant categories can go higher. Payments to hospitals and educational institutions were raised to ₹5 lakh back in December 2023, and from September 2025 the NPCI allowed verified merchants in a set of specific categories, such as capital markets, insurance, and travel, to accept up to ₹5 lakh per transaction as well. The exact per-category caps vary, and your bank or aggregator may set a lower internal limit, so if you sell high-value items, confirm the current limit for your own category rather than assuming ₹5 lakh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cards, netbanking, and wallets: the rest of the menu
&lt;/h2&gt;

&lt;p&gt;UPI covers most consumer payments, but you will still want cards for higher-value purchases, corporate buyers, and anyone paying from abroad. Credit and debit cards, netbanking, and wallets all come as toggles on your aggregator. The one rule every developer needs to internalise here is about storing card details: you cannot. Since the RBI's card-on-file tokenisation mandate took effect on 1 October 2022, neither merchants nor aggregators may store a raw card number, CVV, or expiry date. What you store instead is a &lt;strong&gt;token&lt;/strong&gt;, a meaningless stand-in that is unique to that card, that customer, and your business, generated with the cardholder's consent. In practice your aggregator does the tokenisation and you just keep the token. If you were ever tempted to save a card number in your own database to make repeat checkout smoother, do not. It is both against the rules and a &lt;a href="https://dev.to/blog/website-security-basics-india-2026/"&gt;security&lt;/a&gt; liability you never want to carry.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually costs
&lt;/h2&gt;

&lt;p&gt;Here is the part founders actually care about. The costs split cleanly by method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UPI and RuPay debit cards: zero.&lt;/strong&gt; By law there is no merchant discount rate on these, so accepting them costs you nothing in transaction fees. This is the single biggest reason payments are cheaper to accept in India than almost anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit cards: around 2%.&lt;/strong&gt; Credit card MDR is not capped by the RBI and typically lands near 2% for domestic cards, negotiable once your volume is large.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debit cards other than RuPay: capped and small.&lt;/strong&gt; The RBI caps debit card MDR at roughly 0.4% to 0.9% depending on merchant size, with a per-transaction rupee ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard aggregator pricing: about 2% plus GST.&lt;/strong&gt; Most aggregators quote a flat rate near 2% per successful transaction on domestic cards, netbanking, and wallets, with no setup fee and no annual maintenance on the standard plan. Add 18% GST on the fee itself, so an advertised 2% is closer to 2.36% all-in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;International cards: up to 3%.&lt;/strong&gt; Collecting from foreign cards costs more, commonly up to 3% plus GST.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two things soften the fee. First, the 18% GST on your payment charges is generally claimable as input tax credit if your own sales are taxable, so for a GST-registered business the real cost is the fee, not the fee plus the tax. Second, because UPI is free and dominant, your blended cost of acceptance in India is usually far lower than a card-heavy business in the US or Europe would pay. On settlement, the money does not land instantly. The standard cycle is &lt;strong&gt;T+1&lt;/strong&gt;, one banking day after the transaction, with cards often T+2, and most aggregators offer instant or same-day settlement for an extra fee. Under the 2025 Directions the aggregator holds your funds in escrow and settles on that cycle. If you invoice with GST, make sure your &lt;a href="https://dev.to/blog/gst-e-invoicing-irp-irn-developer-guide-india-2026/"&gt;GST e-invoicing&lt;/a&gt; is wired to the actual settled amounts, not to the gross.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recurring payments and autopay, the part everyone gets wrong
&lt;/h2&gt;

&lt;p&gt;This is where founders coming from Stripe get surprised. In the US you save a card and quietly charge it every month. In India you cannot simply do that. Recurring payments run through the RBI's &lt;strong&gt;e-mandate&lt;/strong&gt; framework, most commonly as UPI Autopay for consumers, and the rules are strict on purpose.&lt;/p&gt;

&lt;p&gt;Setting up a mandate always requires the customer to authenticate once, with a UPI PIN or an OTP, the additional factor of authentication. After that, recurring debits run automatically, but only up to &lt;strong&gt;₹15,000 per transaction&lt;/strong&gt; without the customer re-authenticating each time. Above ₹15,000 the customer has to approve each debit. There is a higher no-authentication ceiling of &lt;strong&gt;₹1 lakh&lt;/strong&gt; for three specific categories: insurance premiums, mutual fund subscriptions, and credit card bill payments. On top of that, the bank must send the customer a &lt;strong&gt;pre-debit notification at least 24 hours before&lt;/strong&gt; each charge, and the customer can cancel any single debit or the whole mandate. As of 2026 these rules sit inside the RBI's consolidated e-mandate framework, which pulled the older card, wallet, and UPI mandate circulars into one place, but the core numbers, ₹15,000, ₹1 lakh for the three categories, and the 24-hour notice, have been stable since 2023.&lt;/p&gt;

&lt;p&gt;The practical upshot for a SaaS or subscription business: your billing has to be built around mandates and their limits, not around silently charging a card. You register a mandate, you handle the asynchronous confirmation and each debit through webhooks, and you reconcile carefully, which is exactly the kind of idempotent, callback-driven flow we wrote about in detail for &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks&lt;/a&gt;. If you are running a multi-tenant product, this billing logic usually lives in the same layer as the rest of your &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/"&gt;per-tenant billing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collecting from customers abroad
&lt;/h2&gt;

&lt;p&gt;If you sell to customers outside India, whether that is a SaaS with global users or an exporter shipping goods, cross-border collection is its own regulated lane. It runs through the RBI's &lt;strong&gt;Payment Aggregator - Cross Border&lt;/strong&gt;, or PA-CB, framework, first set out in a 2023 circular and now part of the 2025 Directions. Foreign currency lands in a dedicated export collection account and is converted and settled to you in rupees, with a per-transaction cap of ₹25 lakh.&lt;/p&gt;

&lt;p&gt;The piece exporters miss is the paperwork that proves the money came in. For that you rely on a &lt;strong&gt;FIRC&lt;/strong&gt;, or its digital form the FIRA, the foreign inward remittance certificate your bank issues, and the &lt;strong&gt;eBRC&lt;/strong&gt; from the DGFT portal, which together prove that an export was realised. You need that proof for GST refunds on zero-rated exports and for any foreign-trade incentives. On providers, Razorpay, Cashfree, and PayU support international collection. PayPal shut its domestic India business back in 2021 and now works cross-border only, so it is an option for receiving from abroad but not for taking rupee payments at home. Stripe is worth flagging: it has been invite-only for new Indian businesses since around 2024, so do not assume you can just sign up. Verify its current status before you plan around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The zero-MDR question everyone asks
&lt;/h2&gt;

&lt;p&gt;Founders often ask how UPI can possibly be free, and whether it will stay that way. The free part is law. Since 1 January 2020, merchant charges on UPI person-to-merchant payments and on RuPay debit cards have been prohibited, under provisions inserted into the Income-tax Act and the Payment and Settlement Systems Act. To offset the cost to banks, the government runs an incentive scheme for low-value UPI at small merchants.&lt;/p&gt;

&lt;p&gt;Whether it stays free is a live debate. Through 2025 and into 2026, the payments industry pushed to bring back a small merchant fee on UPI for large merchants, arguing that free acceptance is not financially sustainable for the banks and networks that run the rail. The Finance Ministry publicly rejected reports of an imminent charge, and a parliamentary committee later suggested the government at least study a tiered model that would exempt small merchants and charge larger ones. As of mid-2026, &lt;strong&gt;nothing binding has changed&lt;/strong&gt;: UPI remains free to accept. But this is the single most likely thing in this article to move, so if a lot of your margin depends on UPI staying free, keep an eye on it.&lt;/p&gt;

&lt;p&gt;One genuine exception already exists. A &lt;strong&gt;RuPay credit card paid through UPI&lt;/strong&gt; is a credit product, not a bank transfer, so it is not covered by the zero-MDR rule. Those transactions are free only up to ₹2,000, and above that they carry a merchant fee. It is a small slice of volume today, but worth knowing that your UPI acceptance is not uniformly free once credit-on-UPI is in the mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we wire this up for clients
&lt;/h2&gt;

&lt;p&gt;When we build payments into a product, the shape is almost always the same. We pick an aggregator based on the client's mix of UPI, cards, and international, integrate its APIs and SDKs, and put the real engineering effort into the parts that go wrong quietly. That means handling &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;webhooks idempotently&lt;/a&gt; so a retried callback never double-credits an order, reconciling settled amounts against orders every day, and never storing a card number because the aggregator's token is all we keep. For subscriptions we build around e-mandates and their limits rather than fighting them. GST invoices are generated on the settled amount, and for exporters we make sure FIRC and eBRC are captured. Most of this lives on a &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; backend where the webhook handling and reconciliation can be tested properly. None of it is exotic, but the difference between a payments integration that just works and one that quietly leaks money is entirely in these unglamorous details.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic checklist to go live
&lt;/h2&gt;

&lt;p&gt;If you are starting from zero, this is the order that works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign up with one aggregator and finish KYC.&lt;/strong&gt; Pick Razorpay, Cashfree, or PayU based on your fees and the features you need. KYC and activation take a few days, so start early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch on UPI and cards first.&lt;/strong&gt; UPI is free and covers most consumers, cards cover the rest. Add netbanking and wallets if your audience actually uses them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build webhook handling and reconciliation.&lt;/strong&gt; Treat every payment callback as something that might arrive twice or out of order, and reconcile settled money against your orders daily. This is the part that protects your revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the aggregator handle tokenisation.&lt;/strong&gt; Never store raw card data. Keep only the token it gives you back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wire GST invoicing to settled amounts.&lt;/strong&gt; Invoice on what actually settled, not the gross, and claim input tax credit on your payment fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you bill recurring, set up e-mandates.&lt;/strong&gt; Design around the ₹15,000 and ₹1 lakh limits and the 24-hour pre-debit notice, and process each debit through webhooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you sell abroad, add a cross-border provider.&lt;/strong&gt; Use a PA-CB-enabled aggregator and capture FIRC and eBRC as your export proof.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test everything in sandbox first.&lt;/strong&gt; Run success, failure, and retry cases before you take a single rupee live.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common questions about accepting payments in India
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need my own payment aggregator licence to accept payments?
&lt;/h3&gt;

&lt;p&gt;No. The RBI licence is for the company that holds and settles funds, which is your aggregator, not you. For virtually every business you sign up with an authorised aggregator like Razorpay, Cashfree, or PayU and use their licence and escrow setup. You would only need your own authorisation if you were building a platform that collects and settles money on behalf of other merchants, and that comes with a net worth requirement of ₹15 crore rising to ₹25 crore plus heavy compliance.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much does it cost to accept online payments in India?
&lt;/h3&gt;

&lt;p&gt;It depends on the method. UPI and RuPay debit cards are free by law. Credit cards run around 2%, and standard aggregator pricing is about 2% plus 18% GST on domestic transactions, with no setup or annual fee on most standard plans. International cards cost up to 3%. Because UPI is both free and the most-used method, most Indian businesses pay a much lower blended rate than a card-heavy business elsewhere would.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I charge a customer's card automatically every month like a US SaaS?
&lt;/h3&gt;

&lt;p&gt;Not directly. India routes recurring payments through the RBI e-mandate framework, usually UPI Autopay. The customer authenticates once to set up the mandate, after which debits run automatically only up to ₹15,000 per transaction, or ₹1 lakh for insurance, mutual funds, and credit card bills, and the bank must notify them 24 hours before each charge. You build your billing around mandates, not around silently charging a saved card.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it true I cannot store customer card numbers?
&lt;/h3&gt;

&lt;p&gt;Yes. Since October 2022 the RBI's tokenisation mandate forbids merchants and aggregators from storing raw card numbers, CVV, or expiry dates. Your aggregator replaces the card with a token that is useless to anyone else, and that token is all you keep. This is a good thing: it removes the single most dangerous piece of data from your servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I collect payments from customers outside India?
&lt;/h3&gt;

&lt;p&gt;Through a cross-border aggregator under the RBI's PA-CB framework. Foreign currency settles to you in rupees, capped at ₹25 lakh per transaction, and you capture a FIRC or FIRA from your bank plus an eBRC from the DGFT portal as proof of the export, which you need for GST refunds and trade incentives. Razorpay, Cashfree, and PayU support this. PayPal works cross-border only, and Stripe has been invite-only for new Indian merchants, so check availability before planning around either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will UPI stay free for merchants?
&lt;/h3&gt;

&lt;p&gt;As of mid-2026, yes, it is still free, and there is no notification changing that. But the industry has been pushing to reintroduce a small merchant fee on UPI for large merchants, and a parliamentary committee has suggested studying a tiered model. Nothing binding has happened yet, but this is the most likely rule in this space to change, so watch it if your margins lean heavily on free UPI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest summary
&lt;/h2&gt;

&lt;p&gt;Accepting payments in India comes down to a few clear ideas. You sign up with one authorised aggregator and switch on the methods you want, and you do not need your own licence. UPI is free and covers most consumers, cards cost around 2% and cannot be stored in raw form, and standard aggregator pricing is roughly 2% plus GST with settlement on a T+1 cycle. Recurring payments run through e-mandates with real limits and a 24-hour notice, so subscription billing has to be designed around them rather than bolted on. Selling abroad means a cross-border provider and some export paperwork. And the whole thing rewards boring engineering: idempotent webhooks, daily reconciliation, tokenised cards, and GST invoices on settled amounts. Get those right and payments become the part of your product you never have to think about again.&lt;/p&gt;

&lt;p&gt;Setting up payments for a new product, or untangling a setup that is leaking money on fees or failing renewals? &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;Message us on WhatsApp&lt;/a&gt; with what you are selling and who you are selling to, and we will tell you honestly which aggregator and which methods make sense, and what it will actually cost you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We build payment flows into web and SaaS products the right way: the correct aggregator for your mix of UPI, cards, and international, webhooks handled idempotently so nothing double-charges, clean daily reconciliation, tokenised cards, e-mandates for subscriptions, and GST-ready invoicing on settled amounts. If you want payments that just work and do not quietly leak money, let us wire it up.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Talk to us about payments&lt;/a&gt;&lt;/p&gt;

</description>
      <category>acceptpaymentsindia</category>
      <category>howtoacceptonlinepaymentsindia</category>
      <category>paymentgatewayindia2026</category>
      <category>paymentgatewayvspaymentaggrega</category>
    </item>
    <item>
      <title>The DPDP Act in 2026: A Practical Compliance Guide for Indian Websites and SaaS</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Sun, 05 Jul 2026 07:16:13 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/the-dpdp-act-in-2026-a-practical-compliance-guide-for-indian-websites-and-saas-399k</link>
      <guid>https://dev.to/buildbyravirai/the-dpdp-act-in-2026-a-practical-compliance-guide-for-indian-websites-and-saas-399k</guid>
      <description>&lt;p&gt;If you run a website or a SaaS product in India, you have probably heard that a big data protection law is coming and that the fines run into hundreds of crores. Both halves of that sentence are true, but the timing and the practical reality are more nuanced than the panic posts on LinkedIn suggest. This is the honest version, written by developers who build and ship Indian products, not a law firm memo and not a compliance-vendor sales pitch.&lt;/p&gt;

&lt;p&gt;Here is the short story. The Digital Personal Data Protection Act, 2023 (the DPDP Act) got Presidential assent back in August 2023, but it sat dormant for two years because the government had not written the Rules that make it work. Those Rules were finally notified in November 2025. So as of mid-2026, the law is real and final, but the obligations most businesses actually care about are not enforceable yet. They switch on in stages, with the main deadline landing around mid-May 2027. You are in the preparation window, not the panic window.&lt;/p&gt;

&lt;p&gt;We think that is genuinely good news, because it means you have time to do this properly instead of bolting on a cookie banner the night before an audit. Below we walk through what the Act is, whether it applies to your business (spoiler: almost certainly yes), the core duties in plain English, how you actually build the technical bits, the penalties, and a realistic plan for what to do this quarter. One disclaimer up front: this is practical guidance to help you get oriented and start work, not legal advice. For a binding opinion on your specific setup, talk to a data protection lawyer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the DPDP Act actually is, and where it stands in 2026
&lt;/h2&gt;

&lt;p&gt;The DPDP Act is India's first proper, standalone data protection law. Before it, the only thing governing personal data online was the Information Technology Act, 2000 and its 2011 SPDI Rules, which are thin and outdated. The DPDP Act replaces that patchy regime with something closer in spirit to the EU's GDPR, though it is leaner and built differently (more on the differences later).&lt;/p&gt;

&lt;p&gt;The law works on a simple pair of roles. If you decide why and how personal data gets processed, you are a &lt;strong&gt;Data Fiduciary&lt;/strong&gt; (GDPR calls this a controller). The person whose data it is, your user, is the &lt;strong&gt;Data Principal&lt;/strong&gt;. Anyone who processes data on your behalf, like your cloud host or your email tool, is a &lt;strong&gt;Data Processor&lt;/strong&gt;. Personal data is defined broadly as any data about an individual who is identifiable from it. Importantly, the Act only covers &lt;strong&gt;digital&lt;/strong&gt; personal data: data collected in digital form, or collected on paper and later digitised. A paper register that never gets typed up is outside the Act, though in practice almost everything ends up digital anyway.&lt;/p&gt;

&lt;p&gt;On status, be precise, because a lot of online write-ups get this wrong. MeitY notified the Digital Personal Data Protection Rules, 2025 in mid-November 2025 (most sources say 13 November, with Gazette publication on 14 November). That notification did not switch everything on at once. Commencement is deliberately phased into three tranches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1 (immediate, from November 2025):&lt;/strong&gt; the definitions and the provisions that set up the Data Protection Board of India. These are live now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2 (around November 2026):&lt;/strong&gt; the Consent Manager registration regime, roughly 12 months after notification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3 (around mid-May 2027):&lt;/strong&gt; the substantive duties that matter most to you, notice and consent, security safeguards, breach reporting, retention and erasure, children's data, Significant Data Fiduciary duties, cross-border transfer, and Data Principal rights. This is roughly 18 months after notification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So as of 5 July 2026, only Phase 1 is in force. The core compliance duties are not yet enforceable. Until they are, the old IT Act and 2011 SPDI Rules still technically govern data protection in the interim. One more honest caveat: the Data Protection Board, the body that will actually adjudicate complaints and hand out penalties, was still being constituted through mid-2026. MeitY only invited applications for its Chairperson and Members in May 2026. So even the enforcement machinery is being built out as we write this. Treat mid-May 2027 as your working deadline, not a date that has already passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this apply to my small business?
&lt;/h2&gt;

&lt;p&gt;Almost certainly yes, and this is the single most common myth we need to kill. There is &lt;strong&gt;no&lt;/strong&gt; small-business, MSME, turnover, or user-count exemption in the DPDP Act. If you collect an email address for a newsletter, store customer phone numbers, run a login system, or drop an analytics cookie that captures an IP address, you are processing digital personal data and you are a Data Fiduciary. A two-person startup in Noida is in scope exactly like a large e-commerce platform.&lt;/p&gt;

&lt;p&gt;There is one wrinkle worth being honest about. Section 17(3) of the Act gives the government the &lt;strong&gt;power&lt;/strong&gt; to exempt certain classes of Data Fiduciary, and it specifically names startups, from some duties like the notice requirement and parts of the erasure and access obligations. But that is only an enabling power. As of mid-2026, the government has not actually issued any such notification. So you cannot plan around a startup exemption that does not exist yet. It may arrive before May 2027, or it may not. Build for full compliance and treat any future relaxation as a bonus.&lt;/p&gt;

&lt;p&gt;The reach is also wider than you might expect. The Act applies to processing inside India, and it applies extra-territorially to businesses &lt;strong&gt;outside&lt;/strong&gt; India that process the data of people in India in connection with offering them goods or services. So a foreign SaaS serving Indian users is squarely covered. If you are an Indian agency or product company serving Indian customers, there is no doubt at all: this is your law.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core obligations in plain English
&lt;/h2&gt;

&lt;p&gt;Strip away the legalese and the DPDP Act asks you to do a handful of sensible things. Here they are the way we would explain them to a founder over coffee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consent and notice.&lt;/strong&gt; The default lawful basis for processing is consent, and consent has to be free, specific, informed, unconditional, and unambiguous, given by a clear affirmative action. In plain terms: no pre-ticked boxes, no burying agreement inside a giant terms-and-conditions blob, no bundling ten unrelated purposes into one "I agree". Every consent request must be preceded or accompanied by a &lt;strong&gt;notice&lt;/strong&gt; that tells the user what data you are collecting, why, how to exercise their rights, and how to complain to the Data Protection Board. The notice has to be a standalone document in clear, plain language, and you must offer it in English or any of the 22 languages in the Eighth Schedule of the Constitution. And withdrawing consent must be as easy as giving it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purpose limitation and minimisation.&lt;/strong&gt; You can only collect the data you actually need for the stated purpose, and you can only use it for that purpose. Want to use the same data for something new? Fresh notice, fresh consent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security safeguards.&lt;/strong&gt; You must protect personal data with reasonable security measures. The Rules spell out a floor: encryption or masking or tokenisation, access controls, logging and monitoring, backups, and one year of retained logs. Failing this is the single most expensive thing you can get wrong under the Act, so it is worth doing well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retention and erasure.&lt;/strong&gt; Delete personal data once the purpose is done or the user withdraws consent, unless a law requires you to keep it. For a few very large classes (big e-commerce, gaming, and social media platforms above specified user thresholds), the Rules add a hard three-year auto-delete clock for inactive users, with 48 hours' advance notice before deletion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breach notification.&lt;/strong&gt; If you have a personal data breach, you must tell every affected user without delay, and give the Data Protection Board an initial intimation followed by a detailed report within 72 hours. There is no "it was minor so we skipped it" exemption. Unlike GDPR, DPDP has no harm threshold: any breach triggers the duty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grievance redressal and a contact point.&lt;/strong&gt; You must publish the business contact of a person (or Data Protection Officer, where applicable) who can answer questions about your processing, and run an effective grievance mechanism that responds within a published timeline of no more than 90 days. Users have to exhaust your grievance process before they can go to the Board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changes for developers
&lt;/h2&gt;

&lt;p&gt;This is the part we care about most, because DPDP compliance is at least half an engineering problem. Here is how the duties above translate into things you build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat consent as versioned data, not a boolean.&lt;/strong&gt; A single &lt;code&gt;agreed = true&lt;/code&gt; column will not cut it. You need a consent object keyed to purpose, so a user can agree to "process my email for order updates" without agreeing to "send me marketing". And you need an audit-grade record of each consent event: the exact notice text or version shown, the purposes chosen, the timestamp, and the affirmative action taken, plus any later withdrawal. Think of it as an append-only consent ledger. If the Board ever asks, you need to reconstruct exactly what a given user saw and chose on a given date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build a real privacy notice, not a wall of text.&lt;/strong&gt; Replace the single "Privacy Policy" link at signup with a purpose-specific, itemised notice: what fields, what purpose, and clickable links to withdraw consent, exercise rights, and complain to the Board. Multilingual delivery is expected, so architect your notice content so it can be served in more than just English.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not forget your existing users.&lt;/strong&gt; For data you already hold, you do not need fresh opt-in. Section 5(2) lets you send a one-time notice to your legacy base covering what you hold and why, and you can keep processing until they withdraw. Practically, that is a one-off email, SMS, WhatsApp, or in-app campaign with embedded withdraw and rights links, and you should log delivery. Plan that campaign now; it is a clean, contained piece of work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build self-serve rights flows (DSAR).&lt;/strong&gt; Users get rights to access a summary of their data, to correct it, and to erase it, plus grievance redressal and a right to nominate someone to act on their behalf if they die or become incapacitated. In code, that means authenticated endpoints for access/export, correction, and deletion, an identity-verification step, an SLA timer or ticketing workflow, and crucially, cascade deletion that propagates to your processors and backups. If you run a multi-tenant product, get this right at the tenant-isolation level; our &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/"&gt;multi-tenant SaaS architecture guide&lt;/a&gt; covers the data-boundary patterns that make per-user erasure actually tractable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nail the security baseline.&lt;/strong&gt; The Rule 6 floor is concrete: encrypt personal data at rest and in transit, enforce least-privilege access with audit logging retained at least 12 months, run monitoring to detect unauthorised access, keep tested backups, and bind your processors contractually to the same standards. If you want a ground-up walkthrough of the fundamentals, we wrote &lt;a href="https://dev.to/blog/website-security-basics-india-2026/"&gt;website security basics for Indian businesses&lt;/a&gt;, and moving your users off shared passwords onto &lt;a href="https://dev.to/blog/passkeys-passwordless-authentication-guide-2026/"&gt;passkeys and passwordless auth&lt;/a&gt; is one of the highest-leverage things you can do to shrink breach risk. If your infra is a mess, our &lt;a href="https://dev.to/services/cloud-devops/"&gt;cloud and DevOps team&lt;/a&gt; does exactly this kind of hardening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Draw a data map.&lt;/strong&gt; DPDP has no explicit GDPR-style "record of processing" requirement, but you cannot satisfy retention, erasure, breach-scoping, or audits without knowing what personal data you hold, where it lives, which processor touches it, and what purpose and retention clock each field carries. So build the inventory anyway. It is the backbone artefact everything else hangs off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put processor contracts in place.&lt;/strong&gt; You can only use a processor under a valid contract, and you stay fully liable for their compliance. So every sub-processor (cloud, analytics, email, payments) needs a data processing agreement that flows down security obligations, breach cooperation to meet your 72-hour clock, help with user requests, and deletion on termination or consent withdrawal. If you handle payments, the same discipline that makes your &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks idempotent and reconciled&lt;/a&gt; is the mindset you want across every processor boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Children's data and Significant Data Fiduciary duties
&lt;/h2&gt;

&lt;p&gt;Two areas carry heavier duties, and both are worth flagging early because they are hard to retrofit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Children.&lt;/strong&gt; Under the DPDP Act a "child" is anyone under 18, which is a much higher bar than GDPR's 13 to 16. Before processing a child's data you need verifiable consent from a parent or lawful guardian, and you have to verify that the consenting adult really is an identifiable adult, using details you already hold, details they provide, or a virtual token tied to verified age and identity from an authorised entity (think a DigiLocker-type credential). On top of that, you are barred from tracking, behavioural monitoring, or targeted advertising directed at children. This is genuinely difficult to implement at scale, partly because robust age verification pushes against data minimisation (you may end up collecting more identity data to prove age), and partly because the authorised-token infrastructure was not widely deployed as of mid-2026. There are carve-outs for specific contexts like healthcare, education, childcare, and safety-related location tracking, but a general website or SaaS gets no relief. The same verifiable-guardian regime extends to persons with disabilities who cannot make legally binding decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Significant Data Fiduciaries (SDFs).&lt;/strong&gt; This is not automatic. The government designates an entity as an SDF by notification, based on factors like data volume, sensitivity, and risk to individuals or to the state. Once designated, you carry extra duties: appoint an India-based Data Protection Officer answerable to your board, appoint an independent data auditor, run a Data Protection Impact Assessment and an audit at least every 12 months, do due diligence that your algorithms do not pose risks to users' rights, and comply with any government order to keep specified data inside India. As of mid-2026 no public list of designated SDFs had been confirmed, so unless you are notified, these duties do not apply to you yet. But if your product is scaling fast, keep this on your radar.&lt;/p&gt;

&lt;h2&gt;
  
  
  How DPDP differs from GDPR, and the cookie-banner myth
&lt;/h2&gt;

&lt;p&gt;If you have GDPR muscle memory, do not just copy-paste it. The two laws share a philosophy but differ in ways that matter operationally.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lawful bases.&lt;/strong&gt; GDPR gives you six, including the flexible "legitimate interests" and "contractual necessity". DPDP gives you effectively two: consent, or a closed, fixed list of "legitimate uses" (things like data a person voluntarily gave you, state functions, legal compliance, medical emergencies, and certain employment purposes). There is no open-ended legitimate-interest ground and no balancing test for private firms. So most private-sector processing in India must be consent-based, which raises the bar on your consent capture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No sensitive-data category.&lt;/strong&gt; GDPR singles out health, biometric, religious, and other special categories for extra protection. DPDP has one uniform standard for all personal data. It layers extra duties by entity (the SDF mechanism) rather than by data type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-border transfers.&lt;/strong&gt; GDPR uses a whitelist (transfer is banned unless the destination is approved). DPDP inverts this: transfer is allowed by default, except to countries the government specifically blacklists. As of mid-2026 no such blacklist had been notified. Sectoral rules like RBI's payment-data localisation still apply independently, and the government can restrict specified data categories, so "free by default" is the headline but not absolute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope.&lt;/strong&gt; DPDP covers only digital personal data. GDPR also covers structured paper filing systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the cookie question, because it is the most misunderstood. DPDP never mentions the word "cookie" and it imposes no EU ePrivacy-style banner mandate. That does not mean cookies are unregulated. Where a cookie or tracker collects personal data (an IP, a device ID, behavioural data) on a consent basis, the general DPDP notice-and-consent machinery applies. Strictly necessary cookies (session, auth, cart) can plausibly ride the "legitimate uses" exception and may not need explicit consent. Analytics, advertising, and behavioural-tracking cookies do need consent. One real difference from the EU: GDPR effectively expects granular, per-vendor toggles, while under DPDP granular consent is best practice but not strictly required, so an all-or-nothing accept/decline is currently acceptable. Cookie walls and coercive designs are out, because consent must be free. Honest caveat: this area is the least settled, drawn mostly from law-firm commentary rather than any Board ruling, and the Board could push toward GDPR-style granularity later. So build for flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The penalties, without the scaremongering
&lt;/h2&gt;

&lt;p&gt;Yes, the numbers are large. The Act's Schedule sets tiered maximum penalties per breach category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up to &lt;strong&gt;₹250 crore&lt;/strong&gt; for failing to take reasonable security safeguards. This is the headline figure and the single most expensive thing to get wrong.&lt;/li&gt;
&lt;li&gt;Up to &lt;strong&gt;₹200 crore&lt;/strong&gt; for failing to notify a personal data breach.&lt;/li&gt;
&lt;li&gt;Up to &lt;strong&gt;₹200 crore&lt;/strong&gt; for breaching the children's-data obligations.&lt;/li&gt;
&lt;li&gt;Up to &lt;strong&gt;₹150 crore&lt;/strong&gt; for a Significant Data Fiduciary breaching its extra duties.&lt;/li&gt;
&lt;li&gt;Up to &lt;strong&gt;₹50 crore&lt;/strong&gt; as a residual catch-all for any other contravention.&lt;/li&gt;
&lt;li&gt;Up to &lt;strong&gt;₹10,000&lt;/strong&gt; on a Data Principal who abuses the system (files false or frivolous complaints, or supplies fake information).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two things to keep in perspective. First, these are maximums per breach, not a fixed fine and not a single aggregate cap, and one incident could in theory trigger more than one head. When the Board sets the actual amount, it weighs the nature, gravity, and duration of the breach, the type of data, whether you took mitigating action, and proportionality. A tiny startup that made a good-faith mistake is not going to be treated like a negligent giant. Second, and this is the calming part: these penalties are not live yet. The Data Protection Board was still being constituted through mid-2026 and no penalty orders had been reported. The enforceable date for the substantive duties is around mid-May 2027. Notably, penalties go to the Consolidated Fund of India; the Act does not give affected individuals a right to compensation, so the remedy runs through the Board, not through damages suits.&lt;/p&gt;

&lt;p&gt;If you get an adverse order later, you can appeal to TDSAT (the Telecom Disputes Settlement and Appellate Tribunal) within 60 days, and the bare Act does not require you to pre-deposit any part of the penalty to appeal. Ignore any blog claiming a 50% pre-deposit rule; that is not in the law.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to actually do this quarter
&lt;/h2&gt;

&lt;p&gt;You have until roughly mid-May 2027 for the main duties, and Consent Manager registration lands around November 2026. That is enough runway to do this calmly if you start now. Here is a realistic order of work for an Indian SMB or startup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Map your data.&lt;/strong&gt; List every place you collect personal data, what fields, why, where it is stored, which processor touches it, and how long you keep it. Nothing else works without this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit your security against the Rule 6 floor.&lt;/strong&gt; Encryption at rest and in transit, access controls, logging with 12-month retention, monitoring, backups. Fix the gaps here first, because this carries the biggest penalty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redesign your signup consent.&lt;/strong&gt; Kill pre-ticked boxes and bundled agreement. Build a purpose-keyed consent record and a plain-language, itemised notice with withdraw and rights links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the withdraw and rights flows.&lt;/strong&gt; A self-serve way to withdraw consent, plus access, correction, and erasure endpoints with cascade deletion to processors and backups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan your legacy-user notice.&lt;/strong&gt; Draft the one-time Section 5(2) notice campaign for your existing database and log delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign processor DPAs.&lt;/strong&gt; Get data processing agreements in place with your cloud, email, analytics, and payment vendors, flowing down security and deletion duties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up breach and grievance processes.&lt;/strong&gt; A breach-detection and 72-hour reporting playbook, and a published grievance contact with a response timeline under 90 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you touch children's data, address age-gating early.&lt;/strong&gt; It is the hardest piece, so do not leave it to the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write it down.&lt;/strong&gt; Keep a simple internal record of what you did and when. If the Board ever asks, demonstrable good-faith effort matters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is not a weekend job, but it is also not a crisis. Most of it is good engineering hygiene you would want anyway. If your team is stretched, this is the kind of retrofit our &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js development team&lt;/a&gt; does regularly: consent ledgers, DSAR endpoints, and security hardening baked into an existing product without tearing it apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions about the DPDP Act
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is the DPDP Act in force in 2026?
&lt;/h3&gt;

&lt;p&gt;Partly. The Act is enacted and the DPDP Rules, 2025 were notified in mid-November 2025, so it is final law. But enforcement is phased. As of mid-2026 only Phase 1 is live (the Data Protection Board framework and definitions). Consent Manager registration comes around November 2026, and the core duties, notice, consent, security, breach reporting, retention, children's data, and Data Principal rights, become enforceable around mid-May 2027. So the substantive obligations are not yet enforceable, but you should be preparing for them now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the DPDP Act apply to my small business or startup?
&lt;/h3&gt;

&lt;p&gt;Yes. There is no size, turnover, or user-count exemption. Any business that processes digital personal data, even just customer emails or login details, is a Data Fiduciary. The Act does let the government carve out startups from some duties in future, but no such exemption had been notified as of mid-2026, so plan for full compliance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need a GDPR-style cookie banner for a DPDP website?
&lt;/h3&gt;

&lt;p&gt;Not exactly. DPDP does not mention cookies and has no ePrivacy-style banner mandate. But if your cookies collect personal data on a consent basis (analytics, advertising, behavioural tracking), you need a DPDP-compliant notice and consent flow, which is similar in spirit but different in form from an EU banner. Strictly necessary cookies can often rely on the legitimate-uses exception. Granular per-vendor toggles are best practice but not strictly required under DPDP as it stands.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the penalties under the DPDP Act?
&lt;/h3&gt;

&lt;p&gt;The Schedule sets maximums per breach: up to ₹250 crore for weak security safeguards, up to ₹200 crore each for breach-notification failures and children's-data violations, up to ₹150 crore for Significant Data Fiduciary failures, and up to ₹50 crore as a catch-all. These are ceilings, not fixed fines; the Board weighs gravity, data type, and mitigation. They are also not being actively enforced yet, since the main duties commence around mid-May 2027 and the Board was still being set up in mid-2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the deadline to become DPDP compliant?
&lt;/h3&gt;

&lt;p&gt;Treat around mid-May 2027 as your working deadline for the core Data Fiduciary duties, roughly 18 months after the November 2025 notification. Consent Manager obligations arrive earlier, around November 2026. Dates shift by a day depending on whether you count from the 13 November signing or the 14 November Gazette publication, so read it as mid-May 2027 rather than a single settled date.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest summary
&lt;/h2&gt;

&lt;p&gt;The DPDP Act is real, final, and it applies to almost every Indian website and SaaS, including small ones. But as of mid-2026 the duties that carry the scary penalties are not enforceable yet, and the Board that would enforce them is still being staffed. That combination is a gift: you have a genuine runway to mid-2027, and most of the work (know your data, secure it, get consent honestly, let people withdraw and delete) is stuff a well-run product should do anyway. Do not panic, do not buy a fear-driven compliance package, and do not believe anyone who tells you it is already fully in force or that startups are automatically exempt. Both are wrong. Start with your data map and your security baseline this quarter, and work through the rest in order.&lt;/p&gt;

&lt;p&gt;A last honest note: this guide is meant to orient you and get engineering moving, not to serve as legal advice. Section and rule numbers and exact dates in the public commentary vary slightly, and the wording that binds you is the official MeitY-published Act and Rules. For a formal opinion on your specific business, work with a data protection lawyer. If you want a hand translating any of this into actual code and infrastructure, that is our day job, and we are happy to do a quick DPDP-readiness look at your site or app. You can &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;message us on WhatsApp&lt;/a&gt; or send us the details and we will tell you honestly where you stand and what is worth doing first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want a straight, no-jargon read on how DPDP-ready your website or SaaS is? We will map your data flows, check your consent and security setup against the Rules, and give you a prioritised fix list with flat INR quotes and GST invoices. No fear-selling, just what actually needs doing before mid-2027.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Get a DPDP-readiness review of your site or app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dpdpactcompliance</category>
      <category>dpdpactforwebsitesindia</category>
      <category>dataprotectionlawindia2026</category>
      <category>dpdpconsentrequirements</category>
    </item>
    <item>
      <title>From Excel to a CRM in 2026: When to Switch, and How to Move Without the Chaos</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Mon, 29 Jun 2026 20:16:56 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/from-excel-to-a-crm-in-2026-when-to-switch-and-how-to-move-without-the-chaos-2lem</link>
      <guid>https://dev.to/buildbyravirai/from-excel-to-a-crm-in-2026-when-to-switch-and-how-to-move-without-the-chaos-2lem</guid>
      <description>&lt;p&gt;Nearly every business starts the same way. Sales lives in a spreadsheet. One tab for leads, one for customers, a column for status, and a colour code only the person who built it really understands. For a while this works beautifully, and anyone who tells you to buy a CRM on day one is usually selling one. A spreadsheet is free, flexible, and instant. The trouble is that it scales right up until it doesn't, and the day it stops working, it stops working quietly. No alarm goes off. You just start losing deals you never knew you had.&lt;/p&gt;

&lt;p&gt;This post is the honest version of the Excel-to-CRM conversation. Not a pitch to rip out your sheet tomorrow, but a clear way to tell when you have genuinely outgrown it, what a migration actually involves, how to move your data over without losing your history, and the part everyone underestimates: how to get your team to actually use the new system instead of drifting back to the old one. We have moved enough businesses off spreadsheets to know where it goes wrong.&lt;/p&gt;

&lt;p&gt;If you want to see what the other side looks like before reading on, you can &lt;a href="https://mirrors-variations-newport-asbestos.trycloudflare.com" rel="noopener noreferrer"&gt;explore the BuildByRavi CRM live demo&lt;/a&gt; and click through a real pipeline, dashboard, and reports. Log in with &lt;strong&gt;&lt;a href="mailto:demo@buildbyravi.com"&gt;demo@buildbyravi.com&lt;/a&gt;&lt;/strong&gt; and the password &lt;strong&gt;Demo@2026&lt;/strong&gt;. Seeing a working pipeline next to your spreadsheet makes the gap obvious faster than any list of features.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a spreadsheet is still the right answer
&lt;/h2&gt;

&lt;p&gt;Let us be fair to the spreadsheet first, because switching too early wastes money and goodwill. If you are one or two people, your deal volume is low enough that nothing slips, you can hold the whole pipeline in your head, and you are still figuring out what your sales process even is, a sheet is perfect. It costs nothing, it bends to whatever you need, and there is no setup. Plenty of healthy businesses run on Excel or Google Sheets far longer than a software salesperson would like to admit. The question is not whether spreadsheets are good or bad. It is whether yours has started leaking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signs you have outgrown the sheet
&lt;/h2&gt;

&lt;p&gt;You rarely decide to leave the spreadsheet. You notice, one painful incident at a time, that it can no longer hold your business. The common signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leads are falling through gaps.&lt;/strong&gt; Someone meant to follow up, the row scrolled out of view, and three weeks later the customer bought from a competitor who simply replied. If you cannot say what the next action is for every open deal, the sheet is failing at its one job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More than one person needs it at once.&lt;/strong&gt; The moment two or three people edit the same file, you get version conflicts, overwritten cells, and the dreaded 'final_v3_latest' copies. Spreadsheets were never built for a team working a shared pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody trusts the numbers.&lt;/strong&gt; When the forecast in the sheet and reality have quietly drifted apart, every meeting turns into an argument about whose copy is right instead of which deals to chase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow-up depends on memory.&lt;/strong&gt; A spreadsheet cannot remind you, message a customer, or chase a quote on its own. If your follow-up only happens when a human remembers, you are losing the deals that needed a second or third touch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot see history.&lt;/strong&gt; Who spoke to this customer last, what was said, what was promised? A sheet shows the current state, not the story, and that story is where deals are won or lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporting takes hours.&lt;/strong&gt; If pulling a simple 'how many deals did we close this month and why did we lose the rest' answer means an evening of copy-paste, the sheet has become the bottleneck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of these is a nuisance. Three or more, week after week, is the spreadsheet telling you it is done. The cost is rarely a dramatic disaster. It is the slow, invisible bleed of deals that quietly went nowhere because the system forgot them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you actually gain by moving
&lt;/h2&gt;

&lt;p&gt;A CRM is not a fancier spreadsheet. The point of moving is to get things a sheet structurally cannot do. Every contact carries its full history, so anyone can pick up a conversation without asking around. Follow-up runs on its own through reminders and automated sequences, so the second and third touch happen whether or not anyone remembers. The whole team works one shared pipeline in real time with no version conflicts. The forecast reflects what is actually happening rather than someone's optimism. And in India specifically, the conversation can live on &lt;a href="https://dev.to/blog/whatsapp-crm-india-2026-setup-cost-features/"&gt;WhatsApp&lt;/a&gt; where deals really close, logged against the right lead instead of trapped on one person's phone. We walked through each of these in detail in &lt;a href="https://dev.to/blog/modern-sales-crm-features-india-2026/"&gt;what a modern sales CRM should do&lt;/a&gt;. The short version: a CRM stops you losing deals you never knew you were losing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a migration actually works
&lt;/h2&gt;

&lt;p&gt;The word 'migration' sounds heavier than it is. For most small and mid-sized businesses, moving off a spreadsheet is a few focused days, not a quarter-long project, as long as you do it in the right order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clean the sheet first.&lt;/strong&gt; A migration is the best chance you will ever get to throw out dead leads, duplicate rows, and the columns nobody has filled in for a year. Move tidy data, not a mess, because garbage that goes in is garbage that comes out, now in a more expensive tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map your columns to fields.&lt;/strong&gt; Decide what each spreadsheet column becomes in the CRM: which is the contact name, the company, the deal value, the stage, the owner. This mapping is the real work, and getting it right is what makes the new system feel like yours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define your stages honestly.&lt;/strong&gt; Your pipeline stages should match how you actually sell, not a generic template. New lead, contacted, quoted, negotiating, won, lost is a fine start. The CRM should fit your process, not force you into someone else's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import in one clean pass.&lt;/strong&gt; Export the sheet to CSV, import it, and check a handful of records by hand to confirm everything landed in the right place. A good CRM import is mostly a file upload and a column match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the old sheet read-only for a bit.&lt;/strong&gt; Do not delete it on day one. Freeze it as a backup and a reference for a few weeks until everyone trusts the new system, then archive it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the whole shape of it. The data move is the easy part. The hard part, the part that decides whether the migration succeeds, comes next.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real challenge is adoption, not data
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable truth that most CRM articles skip: roughly half of CRM rollouts fail, and almost never because the data did not import. They fail because the team quietly goes back to the spreadsheet. If the CRM feels heavier than the sheet it replaced, reps will route around it, keep their real pipeline in WhatsApp and a private tab, and within a month your shiny new system is a half-empty graveyard that everyone lies to in meetings. Avoiding that is mostly about a few human choices, not technology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Make it lighter than the spreadsheet, not heavier.&lt;/strong&gt; If logging a deal takes more clicks than typing a row, you have already lost. The CRM has to remove work, not add it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with only the fields you need.&lt;/strong&gt; A new system with forty mandatory fields is a system nobody fills in. Begin with the handful that matter and add more once the habit sticks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move everyone at once.&lt;/strong&gt; If half the team is on the CRM and half is still on the sheet, both fail. Pick a date, move together, and freeze the old sheet so there is no fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let leadership live in it.&lt;/strong&gt; If the boss still asks for updates over WhatsApp and reads the old spreadsheet, the team learns the CRM is optional. When the forecast everyone is judged on comes out of the CRM, the CRM gets used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show the win early.&lt;/strong&gt; Nothing drives adoption like a rep closing a deal because the system reminded them to follow up. Find that story in the first two weeks and tell it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get adoption right and the migration pays for itself fast. Get it wrong and the cleanest data import in the world ends up in a tool nobody opens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Off-the-shelf, or built around how you sell?
&lt;/h2&gt;

&lt;p&gt;Once you have decided to move, the next fork is which CRM. The global names, Salesforce and HubSpot, are powerful but heavy, priced in dollars, and they make you bend your process to fit their software, which is exactly the friction that kills adoption for a small Indian team. We broke down the real numbers in &lt;a href="https://dev.to/blog/custom-crm-development-india-2026-cost-vs-salesforce-hubspot/"&gt;custom CRM vs Salesforce and HubSpot&lt;/a&gt;. For most businesses coming off a spreadsheet, the better fit is a focused CRM that matches your workflow out of the box, or a custom one shaped around exactly how you sell, with only the modules you need. For marketplace and multi-seller businesses we package this as the &lt;a href="https://dev.to/products/crm-multivendor/"&gt;MultiVendor CRM&lt;/a&gt;. The deciding question is never which CRM has the most features. It is which one your team will actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs to make the move
&lt;/h2&gt;

&lt;p&gt;Cost has two parts, and people usually only think about the first. There is the price of the CRM itself, which for a small team can start free and scale with seats, and there is the cost of the move: cleaning data, mapping fields, importing, and training the team. The second is almost always the bigger number, and it is mostly time rather than money. Set against that is the cost of staying on the spreadsheet, which is invisible precisely because you never see the deals it loses. Most businesses that switch find the migration pays for itself within a couple of months of recovered, no-longer-leaking deals. If you want a rough figure for a custom build shaped around your process, the &lt;a href="https://dev.to/website-cost-calculator/"&gt;cost calculator&lt;/a&gt; gives a starting estimate, and the honest comparison lives in our &lt;a href="https://dev.to/blog/custom-crm-development-india-2026-cost-vs-salesforce-hubspot/"&gt;custom CRM pricing guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we handle the move
&lt;/h2&gt;

&lt;p&gt;When we move a business off a spreadsheet, we treat the migration and the adoption as one job, because a perfect import that nobody uses is a failure. We clean and map your data, set up the stages and fields to match how you actually sell, import your history so nothing is lost, and get your team to a usable pipeline before adding anything fancy. Under the hood, BuildByRavi CRM is a &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/"&gt;multi-tenant&lt;/a&gt; web application on a &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; backend with WhatsApp messaging built in, and when a business needs something specific to how it sells, we customise from what we have already built rather than starting from a blank page. You get a real working system in days, not a six-month software project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions about moving from Excel to a CRM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When should I move from a spreadsheet to a CRM?
&lt;/h3&gt;

&lt;p&gt;When the spreadsheet starts costing you deals rather than saving you money. The clearest signals are leads slipping through the cracks, more than one person needing the file at once, follow-up depending on memory, and reporting taking hours. If one or two of those are happening occasionally, the sheet is probably still fine. If three or more happen every week, you have outgrown it and the leak is real.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will I lose my data when I move to a CRM?
&lt;/h3&gt;

&lt;p&gt;No, a proper migration keeps all of it. You export your spreadsheet to CSV, map each column to a CRM field, and import it in one pass, then spot-check a few records to confirm everything landed correctly. The smart move is to keep the old sheet frozen as a read-only backup for a few weeks until everyone trusts the new system, so there is always a safety net during the switch.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to move from Excel to a CRM?
&lt;/h3&gt;

&lt;p&gt;For most small and mid-sized businesses, the data move is a few days, not months. Cleaning and mapping the data is the real work; the import itself is mostly a file upload and a column match. What takes longer, and matters more, is adoption: giving the team a couple of weeks to build the habit of working in the CRM instead of the sheet. The technology is fast. The behaviour change is what to plan for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do CRM rollouts fail, and how do I avoid it?
&lt;/h3&gt;

&lt;p&gt;They fail because the team quietly returns to the spreadsheet, almost always because the CRM felt heavier than the sheet it replaced. Avoid it by making the CRM lighter to use than Excel, starting with only the fields you truly need, moving the whole team on one date with no fallback, and having leadership read the forecast out of the CRM rather than asking for updates elsewhere. Adoption is a human problem, not a software one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a free CRM enough, or do I need a custom one?
&lt;/h3&gt;

&lt;p&gt;For a small team just leaving a spreadsheet, a focused CRM with a free or low-cost tier is often the perfect first step, and far better than staying on Excel. You move to a custom build when your sales process is specific enough that off-the-shelf tools force you to work around them, or when you need particular integrations like WhatsApp, payments, or your accounting tool. Start with what gets you off the sheet fastest, and customise once you know exactly what you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest summary
&lt;/h2&gt;

&lt;p&gt;Spreadsheets are a great place to start and a terrible place to scale. You have outgrown yours when leads start slipping, the team trips over a shared file, follow-up depends on memory, and nobody trusts the numbers. Moving off it is not the heavy project people fear: clean the data, map the columns, import in one pass, and keep the old sheet as a backup. The part that actually decides success is adoption, so make the CRM lighter than the spreadsheet, move everyone together, and let leadership live in it. Do that, and you stop losing the deals the sheet was quietly dropping.&lt;/p&gt;

&lt;p&gt;Still running sales on a spreadsheet and feeling the cracks? &lt;a href="https://mirrors-variations-newport-asbestos.trycloudflare.com" rel="noopener noreferrer"&gt;See the BuildByRavi CRM live demo&lt;/a&gt; (log in with &lt;strong&gt;&lt;a href="mailto:demo@buildbyravi.com"&gt;demo@buildbyravi.com&lt;/a&gt;&lt;/strong&gt; and password &lt;strong&gt;Demo@2026&lt;/strong&gt;), then &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;message us on WhatsApp&lt;/a&gt; with how your sheet is set up today and we will tell you honestly whether it is time to move and what it would take.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outgrowing the spreadsheet? We move Indian businesses from Excel to a CRM the right way: clean your data, import your history, set up the pipeline around how you actually sell, plug in WhatsApp, and get your team genuinely using it instead of drifting back to the sheet. See what we have built, then let us move you.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/products/crm-multivendor/" rel="noopener noreferrer"&gt;Explore the MultiVendor CRM&lt;/a&gt;&lt;/p&gt;

</description>
      <category>exceltocrm</category>
      <category>spreadsheettocrmmigration</category>
      <category>movefromexceltocrm</category>
      <category>crmforsmallbusinessindia</category>
    </item>
    <item>
      <title>What a Modern Sales CRM Should Do in 2026 (Inside BuildByRavi CRM)</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Sun, 28 Jun 2026 08:20:59 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/what-a-modern-sales-crm-should-do-in-2026-inside-buildbyravi-crm-14o0</link>
      <guid>https://dev.to/buildbyravirai/what-a-modern-sales-crm-should-do-in-2026-inside-buildbyravi-crm-14o0</guid>
      <description>&lt;p&gt;Most businesses do not lose deals because their product is weak. They lose deals because follow-up leaks. A lead comes in, someone means to call back, and three days later it is buried under newer work. The spreadsheet forgot, the sticky note vanished, and a customer who was ready to buy quietly went to whoever replied first. A CRM exists to stop that leak. We built BuildByRavi CRM as our own take on what a modern sales platform should do, and this post is a walk through what is inside it and why each piece earns its place.&lt;/p&gt;

&lt;p&gt;One honest thing up front: a CRM is only worth it if your team actually uses it. Half of CRM rollouts fail because the tool is heavier than the problem it solves, so reps go back to WhatsApp and notebooks. So everything here is built around speed and clarity, not a feature checklist. A good CRM should feel like it removes work, not adds it.&lt;/p&gt;

&lt;p&gt;Prefer to see it rather than read about it? You can &lt;a href="https://mirrors-variations-newport-asbestos.trycloudflare.com" rel="noopener noreferrer"&gt;explore the BuildByRavi CRM live demo&lt;/a&gt; and click through the dashboard, pipeline, and reports as you go. Log in with &lt;strong&gt;&lt;a href="mailto:demo@buildbyravi.com"&gt;demo@buildbyravi.com&lt;/a&gt;&lt;/strong&gt; and the password &lt;strong&gt;Demo@2026&lt;/strong&gt;. Here is what each part does and why it is there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the leads worth chasing
&lt;/h2&gt;

&lt;p&gt;Most pipelines are full of names that will never buy. The cost is not just wasted time, it is that real buyers get the same lukewarm attention as dead ones. BuildByRavi CRM filters prospects by intent signals, so reps spend their hours on the accounts actually showing buying behaviour instead of working the list top to bottom. The job of a CRM is not to store every contact, it is to point you at the next right conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let the follow-up run itself
&lt;/h2&gt;

&lt;p&gt;Follow-up is where humans are weakest and software is strongest. BuildByRavi CRM runs multi-step sales sequences with built-in A/B testing, so the second, third, and fourth touch happen on schedule whether or not anyone remembers. No-code automation workflows handle the busywork around them: move a deal stage, assign an owner, fire a reminder, log an activity, all without a developer. The rep does the human part, the talking, and the system does the parts humans forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet customers where they already are: WhatsApp
&lt;/h2&gt;

&lt;p&gt;In India, deals do not close over email. They close on WhatsApp. A CRM that ignores that is fighting reality, so BuildByRavi CRM has WhatsApp Business messaging built in, with conversations logged against the right lead instead of trapped on one rep's phone. If you are weighing this seriously, we wrote a full guide to a &lt;a href="https://dev.to/blog/whatsapp-crm-india-2026-setup-cost-features/"&gt;WhatsApp CRM setup and cost&lt;/a&gt;, and a primer on &lt;a href="https://dev.to/what-is-whatsapp-business-api/"&gt;what the WhatsApp Business API actually is&lt;/a&gt;. For Indian sales teams, this single feature usually moves the needle more than any other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know which deals are real
&lt;/h2&gt;

&lt;p&gt;Every founder has been burned by a forecast built on optimism. A rep says a deal is 'almost closed' and it slips for the third month running. BuildByRavi CRM uses AI-powered forecasting and deal risk scoring to flag the deals that are stalling, the ones with no recent activity, and the ones the numbers say will not land, so your forecast reflects what is actually happening rather than what everyone hopes. Knowing a deal is at risk while you can still save it is worth more than any dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn from every call
&lt;/h2&gt;

&lt;p&gt;Your best rep does something on calls your newest rep does not, and usually nobody can say exactly what. BuildByRavi CRM records and transcribes calls and turns them into coaching, so the patterns that win become teachable instead of locked in one person's head. A built-in AI co-pilot drafts replies and summarises calls, which means less time typing notes and more time selling. The CRM stops being a filing cabinet and starts being a coach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep your data yours
&lt;/h2&gt;

&lt;p&gt;A CRM holds your most valuable asset: every customer relationship you have. So control is not optional. BuildByRavi CRM has role-based access and audit trails, so the right people see the right data, departing employees cannot walk out with your whole pipeline, and you can always see who changed what. This matters more the moment you grow past a handful of trusted people.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers we built it around
&lt;/h2&gt;

&lt;p&gt;Every feature above exists to move a real metric. These are the targets BuildByRavi CRM is designed to hit, and the reason each module is in the product rather than the goals of any one customer, since your results depend on your team and your market:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Around 2.4x more replies&lt;/strong&gt; from disciplined, tested sequences instead of one-and-done outreach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roughly 38% faster sales cycles&lt;/strong&gt; when follow-up is automatic and the right leads get worked first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forecast accuracy within about 4%&lt;/strong&gt;, so the number you tell your board is closer to the number that lands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A free tier for up to 3 seats&lt;/strong&gt;, so a small team can start without a budget conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup in under an hour&lt;/strong&gt;, because a CRM nobody finishes configuring is a CRM nobody uses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Off-the-shelf or built for your workflow?
&lt;/h2&gt;

&lt;p&gt;Salesforce and HubSpot are powerful, but they are heavy, priced in dollars, and they make you bend your process to fit their software. For a lot of Indian teams that means paying for ninety features to use nine, and fighting the tool every day. A CRM shaped around how your team actually sells wins on the only metric that matters for a CRM: whether people use it. We break down the real cost and trade-offs in &lt;a href="https://dev.to/blog/custom-crm-development-india-2026-cost-vs-salesforce-hubspot/"&gt;custom CRM vs Salesforce and HubSpot&lt;/a&gt;, and for marketplace and multi-vendor businesses we package this as the &lt;a href="https://dev.to/products/crm-multivendor/"&gt;MultiVendor CRM&lt;/a&gt;, a CRM built for businesses that sell through many sellers at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we build it
&lt;/h2&gt;

&lt;p&gt;Under the hood, BuildByRavi CRM is a &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/"&gt;multi-tenant&lt;/a&gt; web application on a &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; backend, with the WhatsApp Business API for messaging, a clean internal API for the automations and integrations, and role-based security throughout. The same engineering we use for client SaaS builds goes into it, which is the point: when we build a custom CRM for your business, you get a real product, not a spreadsheet with a login screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions about getting a CRM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can you build a custom CRM for my business?
&lt;/h3&gt;

&lt;p&gt;Yes. BuildByRavi CRM is our own product, but we also build CRMs shaped around how a specific business sells, with only the modules you need, your fields, your stages, and integrations like WhatsApp, payments, or your accounting tool. The fastest path is usually to start from what we have already built and customise, rather than from a blank page. Tell us how your team sells and we will scope it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to set up a CRM?
&lt;/h3&gt;

&lt;p&gt;BuildByRavi CRM itself is designed to set up in under an hour for a standard sales workflow. A customised CRM built around your exact process takes longer, typically a few weeks depending on integrations, but you are never staring at a blank, generic system wondering where to start. We get you to a usable pipeline first, then refine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it work with WhatsApp?
&lt;/h3&gt;

&lt;p&gt;Yes, WhatsApp Business messaging is built in, because for most Indian sales teams that is where deals actually happen. Conversations are logged against the right lead so nothing lives only on a personal phone. See our &lt;a href="https://dev.to/blog/whatsapp-crm-india-2026-setup-cost-features/"&gt;WhatsApp CRM guide&lt;/a&gt; for how it works in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it cheaper than Salesforce or HubSpot?
&lt;/h3&gt;

&lt;p&gt;For most small and mid-sized Indian businesses, yes, both to run and to actually adopt. Global CRMs charge per seat per month in dollars and pile on features you will not use. A focused CRM, or a custom one built around your workflow, usually costs less over time and gets used more. We lay out the full comparison in &lt;a href="https://dev.to/blog/custom-crm-development-india-2026-cost-vs-salesforce-hubspot/"&gt;custom CRM vs Salesforce and HubSpot&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who owns the data?
&lt;/h3&gt;

&lt;p&gt;You do. With role-based access and audit trails, you control who sees what, and with a custom build you can host it where you want. Your pipeline is your business, and it should never be hostage to a vendor or a single employee's phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest summary
&lt;/h2&gt;

&lt;p&gt;A modern sales CRM in 2026 should do five things well: point you at the leads worth chasing, run the follow-up automatically, meet customers on WhatsApp, tell you honestly which deals are real, and turn your best calls into coaching for everyone else. That is what we built BuildByRavi CRM to do, and it is the same standard we hold a custom CRM to when we build one for a client. A CRM that just stores contacts is a spreadsheet with extra steps. A CRM that grows revenue earns its place.&lt;/p&gt;

&lt;p&gt;Want a CRM built around how your team actually sells? &lt;a href="https://mirrors-variations-newport-asbestos.trycloudflare.com" rel="noopener noreferrer"&gt;Try the BuildByRavi CRM live demo&lt;/a&gt; to see it in action (log in with &lt;strong&gt;&lt;a href="mailto:demo@buildbyravi.com"&gt;demo@buildbyravi.com&lt;/a&gt;&lt;/strong&gt; and password &lt;strong&gt;Demo@2026&lt;/strong&gt;), then &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;message us on WhatsApp&lt;/a&gt; with how you sell today, or use the &lt;a href="https://dev.to/website-cost-calculator/"&gt;cost calculator&lt;/a&gt; for a rough estimate on a custom CRM build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tired of leads leaking out of spreadsheets? We build sales CRMs that surface the right leads, automate follow-up, plug into WhatsApp, and forecast honestly, shaped around how your team actually sells. See what we have built, then let us build yours.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/products/crm-multivendor/" rel="noopener noreferrer"&gt;Explore the MultiVendor CRM&lt;/a&gt;&lt;/p&gt;

</description>
      <category>modernsalescrm</category>
      <category>salescrmfeatures</category>
      <category>crmsoftwareindia2026</category>
      <category>aicrm</category>
    </item>
    <item>
      <title>REST API Design Best Practices 2026: How to Build APIs That Last</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Sun, 28 Jun 2026 07:26:01 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/rest-api-design-best-practices-2026-how-to-build-apis-that-last-32mk</link>
      <guid>https://dev.to/buildbyravirai/rest-api-design-best-practices-2026-how-to-build-apis-that-last-32mk</guid>
      <description>&lt;p&gt;An API is a promise you make to everyone who builds on it. The moment your mobile app, your web frontend, or a partner integrates with it, that shape is hard to change and dangerous to break. Rename a field or change a status code and somewhere an app stops working, often silently, often in production. That is why API design deserves real thought up front, even though it feels invisible to anyone who is not a developer.&lt;/p&gt;

&lt;p&gt;The reassuring part is that good API design is mostly conventions, not cleverness. Decades of people building HTTP APIs have settled on a set of patterns that just work, and following them makes your API predictable, easy to consume, and safe to evolve. This is the practical guide to designing REST APIs in 2026: the conventions that matter, and the mistakes that quietly cost teams later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources and naming
&lt;/h2&gt;

&lt;p&gt;REST is built around resources (things), addressed by URLs, acted on with HTTP methods. So URLs should be nouns, not verbs, and usually plural. Use /users and /users/123, not /getUser or /createUser, because the method already says the action. Nest to show relationships, like /users/123/orders, keep names lowercase with hyphens, and be relentlessly consistent: if it is /products in one place, it is never /product or /items elsewhere. Consistency is the single biggest thing that makes an API feel good to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use HTTP the way it was meant
&lt;/h2&gt;

&lt;p&gt;HTTP already gives you a vocabulary; use it instead of inventing your own. Methods carry meaning: GET reads (and never changes anything), POST creates, PUT and PATCH update, DELETE removes. Status codes carry meaning too, and getting them right is half of good API design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;200 OK&lt;/strong&gt; for a successful read or update, &lt;strong&gt;201 Created&lt;/strong&gt; when you make something new, &lt;strong&gt;204 No Content&lt;/strong&gt; for a successful delete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;400 Bad Request&lt;/strong&gt; for malformed input, &lt;strong&gt;422&lt;/strong&gt; for validation errors, &lt;strong&gt;401 Unauthorized&lt;/strong&gt; when login is needed, &lt;strong&gt;403 Forbidden&lt;/strong&gt; when the user is logged in but not allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;404 Not Found&lt;/strong&gt; for a missing resource, &lt;strong&gt;409 Conflict&lt;/strong&gt; for a clash like a duplicate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;500&lt;/strong&gt; for your own errors, and never use 200 with an error message in the body, because clients cannot tell success from failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Versioning: never break v1
&lt;/h2&gt;

&lt;p&gt;The day you ship an API, assume someone will depend on it forever. Put a version in the path from the start (/v1/users) so you can evolve without breaking existing clients. The rule that saves you: add, do not change. Adding a new field or a new endpoint is safe; renaming a field, removing one, or changing a response shape is a breaking change that belongs in a new version. When you do need breaking changes, ship /v2 and keep /v1 alive long enough for clients to migrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and security
&lt;/h2&gt;

&lt;p&gt;Every real API needs to know who is calling and what they are allowed to do. Use tokens (a JWT or OAuth access token) sent in the Authorization header, over HTTPS only, always. Beyond auth: validate and sanitise every input (never trust the client), apply rate limiting so one caller cannot hammer you, return only the data the caller is allowed to see, and never leak internal details in errors. The &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;billing and payment APIs&lt;/a&gt; we build treat this as non-negotiable, because an API is the front door to your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pagination, filtering, and sorting
&lt;/h2&gt;

&lt;p&gt;Any endpoint that returns a list must paginate. Returning ten thousand records in one response is slow, expensive, and will fall over as your data grows. Offer pagination (offset-based is simplest; cursor-based scales better for large or changing datasets), plus filtering and sorting via query parameters, like /orders?status=paid&amp;amp;sort=-created_at&amp;amp;page=2. Decide on one consistent style and use it on every list endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling that helps
&lt;/h2&gt;

&lt;p&gt;Errors are part of your API, not an afterthought. Return a consistent error shape every time, with a machine-readable code and a human-readable message, so clients can handle failures programmatically. Make messages specific enough to be useful ('email is already registered' beats 'invalid input') without leaking internals like stack traces or SQL. A predictable error format is one of the kindest things you can do for the developers consuming your API, including your own future self.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency and reliability
&lt;/h2&gt;

&lt;p&gt;Networks fail and clients retry, so design for it. GET, PUT, and DELETE are naturally idempotent (calling them twice has the same effect as once). POST is not, which is dangerous for actions like creating an order or taking a payment, where a retry could charge someone twice. The fix is idempotency keys: the client sends a unique key with the request, and your server returns the same result for a repeat of that key instead of doing the work again. We go deep on this for payments in the &lt;a href="https://dev.to/blog/razorpay-webhooks-idempotency-reconciliation-india-2026/"&gt;Razorpay webhooks guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document it, or it does not exist
&lt;/h2&gt;

&lt;p&gt;An undocumented API is unusable, no matter how well designed. Describe it with the OpenAPI (Swagger) standard, which gives you interactive docs developers can read and try, and which many tools can generate clients and tests from. Keep the docs next to the code so they stay current. If a developer cannot understand your API without messaging you, the design is not finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistakes that haunt teams later
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verbs in URLs.&lt;/strong&gt; /createOrder instead of POST /orders. It works, but it fights every convention and tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong status codes.&lt;/strong&gt; Returning 200 for errors, or 500 for a user's bad input. Clients cannot react correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaking changes with no version.&lt;/strong&gt; Renaming or removing a field with no /v2 silently breaks every existing client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No pagination.&lt;/strong&gt; Fine with 50 records, a disaster at 50,000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaky errors.&lt;/strong&gt; Returning stack traces or database errors to the client is both confusing and a security risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rate limiting or input validation.&lt;/strong&gt; The two holes that turn an API into an attack surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent naming.&lt;/strong&gt; /products here, /item there, camelCase in one response and snake_case in another. Death by a thousand small surprises.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we build APIs
&lt;/h2&gt;

&lt;p&gt;We build REST APIs on &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; and &lt;a href="https://dev.to/services/laravel-development/"&gt;Laravel&lt;/a&gt; with these conventions baked in: noun-based versioned routes, correct HTTP semantics, token auth over HTTPS with rate limiting and input validation, consistent pagination and error shapes, idempotency on anything that touches money, and OpenAPI docs that stay in sync with the code. The same disciplined API sits under our &lt;a href="https://dev.to/blog/multi-tenant-saas-architecture-guide-india-2026/"&gt;multi-tenant SaaS&lt;/a&gt; builds and the &lt;a href="https://dev.to/products/crm-multivendor/"&gt;MultiVendor CRM&lt;/a&gt;, because a clean API is what lets a web app, a mobile app, and partners all build on one backend without chaos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions about REST API design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should API endpoints use nouns or verbs?
&lt;/h3&gt;

&lt;p&gt;Nouns, almost always plural. Use POST /orders to create an order and GET /orders/123 to read one, not /createOrder or /getOrder. The HTTP method already expresses the action, so the URL should name the resource. This keeps the API predictable and works naturally with HTTP tools and caches.&lt;/p&gt;

&lt;h3&gt;
  
  
  How should I version my API?
&lt;/h3&gt;

&lt;p&gt;Put the version in the URL path from day one, like /v1/users, and follow the add-do-not-change rule: new fields and endpoints are safe, but renaming or removing anything is a breaking change that needs a new version. Ship /v2 only when you must, and keep /v1 running until existing clients have migrated.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is API idempotency and why does it matter?
&lt;/h3&gt;

&lt;p&gt;An idempotent request has the same effect whether it runs once or many times. It matters because networks fail and clients retry, and a retried POST could create a duplicate order or a double charge. The solution is an idempotency key: the client sends a unique key, and the server returns the same result for repeats instead of redoing the work. It is essential for anything involving payments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need OpenAPI or Swagger?
&lt;/h3&gt;

&lt;p&gt;You need documentation, and OpenAPI (Swagger) is the standard way to provide it. It gives consumers interactive, accurate docs and lets tools generate client libraries and tests. Even a small internal API benefits, because clear docs save endless back-and-forth. An API nobody can understand without asking you is not finished.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST or GraphQL?
&lt;/h3&gt;

&lt;p&gt;For most APIs, REST is the simpler, more widely understood choice and is what we default to. GraphQL shines when clients need to fetch many related things in flexible shapes and you want to avoid over-fetching, common in complex apps with many screens. Pick REST for straightforward resource access; consider GraphQL when query flexibility genuinely earns its extra complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest summary
&lt;/h2&gt;

&lt;p&gt;Good REST API design is mostly discipline, not genius: name resources as nouns, use HTTP methods and status codes correctly, version from day one and never break v1, secure it with token auth and validation, paginate lists, return consistent errors, make money-touching actions idempotent, and document it with OpenAPI. Get these right and your API is a pleasure to build on and safe to grow. Skip them and you inherit breaking changes, security holes, and confused integrators.&lt;/p&gt;

&lt;p&gt;Building or fixing an API and want it done to last? &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;Send us a WhatsApp message&lt;/a&gt; with what you are building, or the &lt;a href="https://dev.to/website-cost-calculator/"&gt;cost calculator&lt;/a&gt; gives a rough estimate for an API or backend build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building an API that a web app, mobile app, or partners will depend on? We design and build REST APIs in Noida and Gurgaon on Node.js and Laravel: clean resource design, versioning, token auth, idempotency, pagination, and OpenAPI docs. Built to last, not to rewrite.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Scope an API build&lt;/a&gt;&lt;/p&gt;

</description>
      <category>restapidesign</category>
      <category>restapibestpractices</category>
      <category>apidesignbestpractices2026</category>
      <category>howtodesignarestapi</category>
    </item>
    <item>
      <title>How to Choose the Right Tech Stack for Your Startup in India 2026 (A No-Hype Guide)</title>
      <dc:creator>Ravi Rai</dc:creator>
      <pubDate>Fri, 26 Jun 2026 05:53:33 +0000</pubDate>
      <link>https://dev.to/buildbyravirai/how-to-choose-the-right-tech-stack-for-your-startup-in-india-2026-a-no-hype-guide-2m5d</link>
      <guid>https://dev.to/buildbyravirai/how-to-choose-the-right-tech-stack-for-your-startup-in-india-2026-a-no-hype-guide-2m5d</guid>
      <description>&lt;p&gt;Every project starts with the same question: what should we build it on? And the moment you search it, the internet hands you a hundred confident, contradictory answers. Use Next.js. No, use WordPress. Real startups use React Native. Actually, Flutter. Everyone is right and everyone is wrong, because they are all answering a question you did not ask: what is best in the abstract, instead of what is best for you.&lt;/p&gt;

&lt;p&gt;The right tech stack does not come from a trend or from whatever your last developer happened to know. It comes from your constraints: what you are building, your budget, your timeline, and crucially, who maintains it after launch. This is the no-hype guide for Indian founders in 2026: the questions that actually decide your stack, the sensible default for each kind of project, and the mistakes that quietly cost you later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wrong way to choose a stack
&lt;/h2&gt;

&lt;p&gt;Three traps catch most people. The first is choosing by hype: picking whatever is loudest on Twitter or in a YouTube title, regardless of whether it fits a small business. The second is choosing by whoever you hired: a freelancer who only knows WordPress will tell you everything is a WordPress job, and a React enthusiast will build a single-page app for a brochure site. The third is choosing by 'what big companies use': Netflix's architecture is the worst possible template for your five-page site or your first MVP. None of these start from your actual situation, which is the only place a good answer can come from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five questions that actually decide it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What are you actually building?&lt;/strong&gt; A marketing site, an online store, a web app or SaaS, and a mobile app are four different problems with four different right answers. Be precise about which one you have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is the budget?&lt;/strong&gt; A custom build and a template on a builder are an order of magnitude apart. The honest stack depends on what you can spend now and later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is the timeline?&lt;/strong&gt; Need to be live in two weeks, or building a serious product over months? Speed-to-launch favours different tools than long-term flexibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who maintains it after launch?&lt;/strong&gt; The most ignored question, and the most important. A stack your team (or a local developer) can actually maintain beats a clever one nobody can touch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How complex and how big will it get?&lt;/strong&gt; A simple site and a multi-tenant SaaS with real scale need very different foundations. Build for where you are heading, not three startups beyond it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer those five honestly and the choice narrows itself. Most bad stack decisions come from skipping straight to the tool before answering these.&lt;/p&gt;

&lt;h2&gt;
  
  
  The right default for each kind of project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A marketing or content website
&lt;/h3&gt;

&lt;p&gt;If it is mostly pages and a blog and a non-technical person updates it, WordPress is still a sensible default for the easy editing. If speed, security, and scale matter more, a modern &lt;a href="https://dev.to/services/nextjs-development/"&gt;Next.js&lt;/a&gt; build wins on performance and maintenance. We compared them directly in &lt;a href="https://dev.to/blog/wordpress-vs-nextjs-indian-small-business-2026/"&gt;WordPress vs Next.js for Indian small businesses&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  An online store
&lt;/h3&gt;

&lt;p&gt;For most first stores, Shopify gets you selling fast with the least maintenance. WooCommerce fits if you live in WordPress already or need deep customisation, and a custom or headless build makes sense at scale. The trade-offs are in &lt;a href="https://dev.to/blog/shopify-vs-woocommerce-india-2026/"&gt;Shopify vs WooCommerce&lt;/a&gt; and &lt;a href="https://dev.to/blog/wordpress-vs-shopify-ecommerce-2026/"&gt;WordPress vs Shopify&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A web app or SaaS
&lt;/h3&gt;

&lt;p&gt;This is custom territory: a &lt;a href="https://dev.to/services/nextjs-development/"&gt;Next.js or React&lt;/a&gt; front end with a &lt;a href="https://dev.to/services/laravel-development/"&gt;Laravel&lt;/a&gt; or &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; backend is our default, chosen by the team that will maintain it. Start with the smallest version that proves people will pay, as in &lt;a href="https://dev.to/blog/saas-mvp-development-india-2026-cost-stack-timeline/"&gt;how to build a SaaS MVP&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A mobile app
&lt;/h3&gt;

&lt;p&gt;Cross-platform is almost always the right call for a startup: one codebase for iOS and Android. The choice is &lt;a href="https://dev.to/blog/flutter-vs-react-native-indian-startups-2026/"&gt;Flutter vs React Native&lt;/a&gt;, which we build with on the &lt;a href="https://dev.to/services/flutter-react-native-development/"&gt;mobile side&lt;/a&gt;, and the right pick depends on your team and whether you also have a web app.&lt;/p&gt;

&lt;h3&gt;
  
  
  You just need a site, fast and cheap
&lt;/h3&gt;

&lt;p&gt;Be honest if you are at the validate-the-business stage. A website builder can be the smart short-term move, covered in &lt;a href="https://dev.to/blog/website-builder-vs-custom-website-india-2026/"&gt;website builder vs custom&lt;/a&gt; and &lt;a href="https://dev.to/blog/ai-website-builders-vs-hiring-developer-india-2026/"&gt;AI website builders vs hiring a developer&lt;/a&gt;. Move to a custom build you own once revenue justifies it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistakes that cost you later
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Over-engineering.&lt;/strong&gt; Building for a million users you do not have yet. The cost is real now; the scale is hypothetical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choosing a stack nobody local can maintain.&lt;/strong&gt; An exotic framework feels clever until your developer leaves and nobody in your city can pick it up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Picking by trend.&lt;/strong&gt; The hot tool of this year is the legacy burden of the next. Choose proven, well-supported tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring who maintains it.&lt;/strong&gt; If you do not know who updates and fixes it after launch, you have not finished choosing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock-in you did not notice.&lt;/strong&gt; Some platforms make it painful to leave. Know how you would move before you commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our default stacks, and why
&lt;/h2&gt;

&lt;p&gt;After enough builds you stop chasing novelty and settle on tools that are fast, hireable, and boring in the best way. Ours: &lt;a href="https://dev.to/services/nextjs-development/"&gt;Next.js and React&lt;/a&gt; for sites and web apps (great performance, huge talent pool), &lt;a href="https://dev.to/services/shopify-development/"&gt;Shopify&lt;/a&gt; for most stores, &lt;a href="https://dev.to/services/laravel-development/"&gt;Laravel&lt;/a&gt; or &lt;a href="https://dev.to/services/nodejs-development/"&gt;Node.js&lt;/a&gt; for backends, and &lt;a href="https://dev.to/services/flutter-react-native-development/"&gt;Flutter or React Native&lt;/a&gt; for mobile. We pick from these based on your five answers above, not the other way around, and we will happily talk you out of a heavier stack than you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions about choosing a tech stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best tech stack for a startup?
&lt;/h3&gt;

&lt;p&gt;There is no single best stack, only the best fit for your project, budget, timeline, and maintenance situation. For a web app or SaaS, a Next.js or React front end with a Laravel or Node.js backend is a strong, hireable default. For a store, Shopify; for a content site, WordPress or Next.js; for mobile, Flutter or React Native. Decide by your constraints, not by what is trending.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use what is popular or what my developer knows?
&lt;/h3&gt;

&lt;p&gt;Lean toward what can be maintained, which usually means a proven, widely-known stack rather than the newest one or a niche one only your current developer understands. Popularity matters mainly because it means more developers can maintain and extend the project later. Avoid both blind trend-chasing and being locked to one person's favourite tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is WordPress still a good choice in 2026?
&lt;/h3&gt;

&lt;p&gt;Yes, for the right job. WordPress remains excellent for content-led sites where non-technical people update pages, as long as it is maintained. For high-performance sites, web apps, or anything that needs to scale, a modern framework like Next.js is usually the better foundation. It depends on the project, not on WordPress being good or bad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I change my tech stack later?
&lt;/h3&gt;

&lt;p&gt;You can, but it costs time and money, so the goal is to choose well enough that you do not have to soon. Migrations are normal as a business grows (a builder to a custom site, a monolith to something more scalable), and a good team plans the move with redirects and data migration. Choosing a maintainable, non-locked-in stack makes any future move far cheaper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need a different stack for mobile and web?
&lt;/h3&gt;

&lt;p&gt;Often you can share a lot. A web app and a cross-platform mobile app can share a backend and API, and React Native shares language and tooling with a React or Next.js web app, which is one reason teams pick it. Flutter is a separate toolchain but excellent for polished apps. If web and mobile both matter, factor that into the choice from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest summary
&lt;/h2&gt;

&lt;p&gt;Choosing a tech stack is not about finding the objectively best technology, it is about matching the tool to your project, budget, timeline, and the people who will maintain it. Name what you are building, answer the five questions honestly, pick the sensible default for that category, and avoid the traps of hype, over-engineering, and stacks nobody local can maintain. Boring, proven, hireable tools win far more often than exciting ones.&lt;/p&gt;

&lt;p&gt;Not sure which way to go for your project? &lt;a href="https://wa.me/917428919927" rel="noopener noreferrer"&gt;Send us a WhatsApp message&lt;/a&gt; with what you are building and your constraints, and we will give you an honest recommendation, or the &lt;a href="https://dev.to/website-cost-calculator/"&gt;cost calculator&lt;/a&gt; gives a rough estimate once you know the shape of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stuck on what to build your product on? We help Indian founders choose a tech stack that fits the project and the budget, not the trend, then build it: Next.js, Shopify, Laravel, Node.js, Flutter, and React Native. Honest advice first, in Noida and Gurgaon.&lt;/strong&gt; → &lt;a href="https://www.buildbyravirai.com/contact/" rel="noopener noreferrer"&gt;Get an honest stack recommendation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>howtochooseatechstack</category>
      <category>besttechstackforstartup</category>
      <category>techstackforwebapp</category>
      <category>webdevelopmentstackindia</category>
    </item>
  </channel>
</rss>
