Our finance team at Softechinfra was burning 9 hours a week on the morning Tally-vs-Razorpay reconciliation: download settlements CSV, paste into Excel, VLOOKUP against Tally vouchers, manually flag mismatches, email Hrishikesh. We rebuilt it as a 16-node n8n workflow that runs at 8:15 am every weekday. The output: a Slack message in #finance-recon listing the previous day's settlement, matched vouchers, and any mismatch with the suggested fix. This post is the exact build, pulled from our own production n8n instance, with the four traps that quietly burn your reconciliation time if you skip them.
TL;DR — what this workflow does in 60 words
At 8:15 am IST, n8n calls Razorpay's settlements API for the previous day. It fetches the matching payments via the payments API, pulls the corresponding Tally sales vouchers via TallyPrime ODBC, matches by amount + reference, and posts the result to Slack. Mismatches get an "investigate this" Slack thread with one-click links.
Why this matters now — November 2025
Razorpay's n8n node shipped in mid-2025, removing the need for custom HTTP-Request glue code. Tally Prime 5.0 (released Q3 2025) added a JSON-over-HTTP API alongside the legacy XML format, which means the data shape on both sides is finally clean enough to script against without 200 lines of XML parsing. The integration was technically possible in 2024; it became boringly reliable in November 2025.
The other trigger for us: Diwali 2025 settlement volumes were 23% higher than 2024 (per Datum Intelligence's wrap-up), which meant our finance team's manual reconciliation went from 5 hours a week to 9. The pain was loud enough to justify the build.
The architecture, in one diagram
The 5-step pipeline in plain English
DIY walkthrough — build it yourself in 4 hours
Below is the full build, in the order we shipped it. Each step has a verification check before moving on.
The matcher code (paste this in)
This is the JavaScript that runs in the Code node. We open-source it under MIT — copy it.
// Inputs (set in node Input Variables):
// - settlements: array of { id, amount, payments: [{ id, amount, order_id, ... }] }
// - vouchers: array of { date, voucherNumber, amount, narration, ... }
const TOLERANCE_PAISE = 50; // ₹0.50 — handle GST rounding
const matched = [];
const mismatched = [];
const unmatchedPayments = [];
const unmatchedVouchers = new Map(
vouchers.map(v => [v.voucherNumber, v])
);
for (const settlement of settlements) {
for (const payment of settlement.payments) {
// Razorpay amount is in paise; Tally amount in rupees with decimals
const paymentAmountPaise = payment.amount;
const candidateVouchers = vouchers.filter(v => {
const voucherAmountPaise = Math.round(v.amount * 100);
const amountDelta = Math.abs(voucherAmountPaise - paymentAmountPaise);
const narrationMatch = v.narration && v.narration.includes(payment.id);
return amountDelta <= TOLERANCE_PAISE && narrationMatch;
});
if (candidateVouchers.length === 1) {
matched.push({
payment_id: payment.id,
voucher_number: candidateVouchers[0].voucherNumber,
settlement_id: settlement.id,
amount: payment.amount / 100,
});
unmatchedVouchers.delete(candidateVouchers[0].voucherNumber);
} else if (candidateVouchers.length === 0) {
unmatchedPayments.push({
payment_id: payment.id,
amount: payment.amount / 100,
order_id: payment.order_id,
razorpay_url: `https://dashboard.razorpay.com/app/payments/${payment.id}`,
suspected_cause: 'No Tally voucher with matching amount + payment_id in narration',
});
} else {
mismatched.push({
payment_id: payment.id,
candidates: candidateVouchers.map(v => v.voucherNumber),
amount: payment.amount / 100,
suspected_cause: 'Multiple Tally vouchers match — likely duplicate posting',
});
}
}
}
return [{
json: {
matched,
mismatched,
unmatched_payments: unmatchedPayments,
unmatched_vouchers: Array.from(unmatchedVouchers.values()),
summary: {
total_payments: settlements.flatMap(s => s.payments).length,
matched_count: matched.length,
mismatched_count: mismatched.length,
match_rate: matched.length / settlements.flatMap(s => s.payments).length,
},
},
}];The narration regex matches because our finance SOP requires every Tally sales voucher posted from a Razorpay payment to have the payment_id in the narration field. This is a one-line addition to your sales-voucher creation script in Tally — and it pays back forever.
The 4 traps (each one cost us a day to debug)
Trap #1: GST rounding makes amounts disagree by ₹0.01 to ₹0.50. Razorpay reports amounts in paise. Tally rounds to 2 decimal places. CGST + SGST split causes off-by-one paise. Hardcode a ₹0.50 tolerance. Strict equality matching loses 12-18% of legitimate matches.
Trap #2: T+2 settlement cycle means yesterday's API call returns nothing on Mondays. Razorpay's standard settlement is T+2 business days. A Monday morning cron looking at "yesterday" (Sunday) finds zero settlements. Our cron looks at "T-2 calendar days" instead, with logic to skip weekends. The first Monday we deployed this without that logic, finance got an empty Slack message and panicked for 40 minutes.
Trap #3: Tally's ODBC connection times out under heavy load. If you query Tally during a heavy book-closing (month-end), the ODBC driver returns "connection refused" or hangs. Wrap the Tally call in a 90-second timeout with two retries spaced 30 seconds apart. We learned this on November 1, when finance was closing October books while our cron tried to query — and the workflow silently failed for two days before we noticed.
Trap #4: Razorpay returns settlements in pagination of 100, finance does ~280/day. The settlements API returns max 100 records per page. If you do not paginate, you silently miss everything beyond 100. n8n's Razorpay node handles this if you set "Return All" to true. Verify this is on. We caught this one because the unmatched-vouchers list was suspiciously long for two weeks before we figured out the API was clipping settlements.
Cost breakdown
| Component | Provider |
|---|---|
| n8n self-hosted | Hetzner CX22 (2 vCPU, 4 GB RAM) |
| n8n license | n8n (self-hosted is free for fair use) |
| Razorpay API | Read-only API calls (free) |
| Tally Prime ODBC | Bundled with Tally Prime license |
| Slack workspace | Free tier (90-day message retention) |
| Backup VPS storage | Hetzner snapshots |
The Hetzner CX22 is a shared cost — the same instance runs four other Softechinfra workflows. If you are spinning up a CX22 just for this one workflow, you carry the whole box. Either way, it is a fraction of the labour cost it replaces.
For comparison: Zapier's equivalent workflow at the same volume costs more each month, and so does Make.com. n8n self-hosted is the right choice once you have a developer comfortable with Docker.
Comparison: managed vs self-hosted automation tools
| Tool | Setup time | When to pick it |
|---|---|---|
| n8n self-hosted | 4 hrs (with Docker familiarity) | You have a developer; you'll add 5+ workflows in year 1 |
| n8n Cloud Pro | 2 hrs | You don't want to operate Docker; team will use the editor heavily |
| Make.com | 3 hrs | Simpler workflows, better visual editor, no code-node need |
| Zapier | 2 hrs | Already paying for Zapier for other flows; not budget-sensitive |
| Custom Python script + cron | 2 days | You want full control; comfortable owning the maintenance |
We picked n8n self-hosted for our own books because we run 14 workflows on the same instance and the per-flow cost is the lowest. For a one-workflow client, Make.com is often the better start.
When NOT to build this workflow
Three cases where the manual reconciliation is fine.
Your monthly Razorpay volume is under ₹4 lakh. At that scale, an SDR or finance assistant can reconcile manually in 35 minutes a week. The 4 hours of build time is not paid back inside a year.
Your CA does the reconciliation. Some external CAs charge a flat monthly fee that includes reconciliation. The workflow does not save them time (they have their own tooling) and does not save you money. Skip.
You are still on Tally ERP 9 (not Tally Prime). The ODBC layer in ERP 9 is older, more brittle, and the field naming differs. The build effort doubles. Either upgrade to Tally Prime (which we recommend for its API and faster ODBC) or skip.
Real example — our own books
We dogfooded this for 6 weeks before writing the post. Across that window:
The 4 mismatched cases were all duplicate Tally entries posted manually by finance during the rush of October's GST filing. The 2 unmatched were Razorpay refunds that had not been booked as Tally credit notes yet — caught within 24 hours of happening, instead of at month-end.
Time saved: 8.7 hours/week of finance team labour, repurposed to vendor follow-ups and credit-note processing. The build paid back in 9 working days.
The Reddit thread on r/IndiaBusiness where founders share Razorpay reconciliation horror stories is depressingly long — the same patterns we hit show up there in dozens of variations. The fix is more boring than people think.
A common question we get about Tally ODBC
"Is the ODBC connection secure to expose to a remote n8n instance?"
The honest answer: barely, if you are careful. Tally's ODBC was designed for LAN use, not internet exposure. Three things to do.
1. VPN, not public IP. Run a Wireguard or Tailscale tunnel between your Hetzner n8n instance and the Tally machine. The Tally ODBC port is never on the public internet.
2. Read-only ODBC user. Tally Prime supports user-level access controls. Create a user with read-only voucher access; use that for n8n credentials. The damage from a compromised credential is limited.
3. Daily snapshot of Tally data. Independent of the workflow, take a daily Tally backup to a separate location. If the workflow ever writes (which it should not, but human error happens), you can roll back.
For higher-volume or multi-entity setups, we recommend Tally on Cloud hosted with a managed provider. The ODBC concerns go away because the provider handles network isolation.
Pre-deploy checklist
- n8n self-hosted on Hetzner with HTTPS via Caddy or Cloudflare Tunnel
- Razorpay test-mode API keys verified in n8n credentials
- Razorpay live-mode API keys swapped in only after 7 days of test-mode runs
- Tally Prime ODBC enabled on port 9000, accessible only via VPN tunnel
- Read-only Tally user created for the n8n credential
- Matcher node tested with 50+ historical payments (manually verified output)
- Cron set to 8:15 IST, weekdays only (handles weekend settlement gap)
- Razorpay "Return All" set to true on the settlements node
- Tally query wrapped in 90s timeout + 2 retries spaced 30s apart
- Error branch posts to #ops-alerts, escalates to PagerDuty after 2 failures
- Slack channel #finance-recon created, finance team subscribed
- Notion database for mismatch tickets linked from Slack messages
What we'd build next
If you have the workflow running for 6 weeks, three add-ons add real value.
Add HDFC/ICICI bank statement reconciliation. Every Razorpay settlement lands in your nominated bank account 1-3 business days later. Add a third data source: bank statement (via BankBazaar's open banking API or by emailing yourself the daily statement PDF and using OCR) and triple-match Razorpay → bank → Tally.
Add a "fraud spike" detector. Compare today's settlement size to the 14-day median. If >3x or <0.4x, post a high-priority Slack alert. Catches both fraud and degradation quickly.
Add an AI summarizer for the mismatch thread. Pipe the mismatch detail into Claude Haiku with the prompt "Summarise this Razorpay-Tally mismatch in one sentence for a CFO". The Slack message reads "Mismatch: Razorpay payment pay_4XYZ for ₹14,250 has no matching Tally voucher; closest candidate is voucher #1842 at ₹14,255 (₹5 GST rounding)". Saves finance another 30 seconds per mismatch.
FAQ
Can I use this with Zoho Books instead of Tally?
Yes — replace the ODBC node with Zoho Books' API. The matcher logic is identical. Zoho's API is cleaner than Tally ODBC, so the workflow ends up at 14 nodes instead of 16.
What about RazorpayX (the business banking arm)?
RazorpayX has its own Tally integration for vendor payments and account statements. If you use RazorpayX for your business banking, that integration handles part of this flow natively. The workflow above is for the merchant-side Razorpay (payments collection), which is a different system.
How do I handle refunds in the matcher?
Refunds come through Razorpay's payments API as negative-amount entries (or in a separate refunds endpoint depending on API version). Tally posts them as credit notes, not sales vouchers. Add a second matcher path: query Tally credit notes by date, match by refund_id in narration. We omitted this in the v1 workflow because our refund volume was <10/month — added it in v2 four weeks later.
Does this workflow work for SaaS subscriptions?
Yes, but the Tally voucher logic is different. SaaS-subscription vouchers tend to be created by a different Tally script (often automated from your billing system). The matcher needs to know which voucher type to look at — add a voucher_type filter in the Tally query.
What if my Razorpay account has multiple sub-merchants (Razorpay Route)?
The settlements API returns the merchant_id field. Add a filter step in n8n that splits the workflow per merchant_id before matching. Each sub-merchant needs its own Tally book; the matcher then runs once per (merchant, day) pair.
Can n8n handle high-volume reconciliation (10,000+ payments/day)?
Yes, but the Tally ODBC becomes the bottleneck before n8n does. At that volume we recommend skipping Tally Prime entirely — move to Zoho Books or a custom Postgres ledger. n8n itself happily processes 100k+ records per workflow on the CX22.
Is there an open-source template for this workflow?
We are publishing the n8n JSON export under MIT in our GitHub example repo by end of November 2025. Email contact@softechinfra.com if you want early access — we share it with serious askers in advance.
Where this fits in our work
We build automation flows like this for our AI automation clients regularly — finance reconciliation, lead enrichment, sales rollups, vendor onboarding. The pattern is the same: pick the boring grunt task that eats 6+ hours of a salaried role per week, build a 4-16 node n8n workflow that runs on a small self-hosted box, save the time, repurpose the human.
This post was written by Hrishikesh Baidya, our CTO, who built and runs this workflow against our own books. For an SMB e-commerce build where the same automation philosophy was applied to lead capture, see our Radiant Finance case study.
For more on the BFCM-day stack checks that pair with this reconciliation flow, see our Nov 27 Black Friday post. For the Diwali post-mortem that explains why these workflows became urgent in 2025, see Diwali 2025 Was the ₹60,700 Cr Week.
Want This Auto-Reconciliation Flow Built for Your Finance Team?
We ship this n8n + Tally Prime + Razorpay workflow as a fixed-scope 5-day engagement for SMB-scale Tally Prime setups. Includes the workflow, hosting setup on your or our Hetzner instance, finance-team training session, and 30 days of bug-fix support. Suitable for SMBs doing ₹50 lakh to ₹15 Cr in monthly Razorpay volume.
Book a 30-min Build CallEmail contact@softechinfra.com if you want the n8n workflow JSON export before our call — we share it with anyone serious about building this themselves.
