How to Send JSON Data to TallyPrime
Convert standard JSON webhooks into Tally XML strings and automate data entry. Perfect for Shopify, Razorpay, or n8n integrations.
The Problem with JSON and Tally
When a customer buys a product on your Shopify store or makes a payment via Razorpay, those platforms send out a JSON Webhook. However, TallyPrime does not understand JSON. It only accepts XML POST requests on its HTTP server.
To integrate the two, we must build a translator (Middleware) that catches the JSON webhook and maps it into Tally's exact XML structure.
What we will build
An Express.js endpoint that accepts a JSON POST request representing a "Sale", converts it into Tally Voucher XML, and pushes it directly into Tally.
Step 1: The Incoming JSON Payload
Let's assume your external app sends this simple JSON object when a sale happens:
{
"orderId": "INV-1024",
"customerName": "Rohan Sharma",
"amount": 5000,
"date": "2026-07-09"
}Step 2: The XML Mapping Function
We need a JavaScript function that takes this JSON and injects it into a Tally XML template for an Accounting Voucher.
const mapJsonToTallyXml = (data) => {
// Format date for Tally (YYYYMMDD)
const tallyDate = data.date.replace(/-/g, '');
return `<ENVELOPE>
<HEADER>
<TALLYREQUEST>Import Data</TALLYREQUEST>
</HEADER>
<BODY>
<IMPORTDATA>
<REQUESTDESC>
<REPORTNAME>Vouchers</REPORTNAME>
</REQUESTDESC>
<REQUESTDATA>
<TALLYMESSAGE>
<VOUCHER VCHTYPE="Sales" ACTION="Create">
<DATE>${tallyDate}</DATE>
<VOUCHERTYPENAME>Sales</VOUCHERTYPENAME>
<PARTYLEDGERNAME>${data.customerName}</PARTYLEDGERNAME>
<NARRATION>Order ID: ${data.orderId}</NARRATION>
<!-- Debit Customer -->
<ALLLEDGERENTRIES.LIST>
<LEDGERNAME>${data.customerName}</LEDGERNAME>
<ISDEEMEDPOSITIVE>Yes</ISDEEMEDPOSITIVE>
<AMOUNT>-${data.amount}</AMOUNT>
</ALLLEDGERENTRIES.LIST>
<!-- Credit Sales Account -->
<ALLLEDGERENTRIES.LIST>
<LEDGERNAME>Sales Account</LEDGERNAME>
<ISDEEMEDPOSITIVE>No</ISDEEMEDPOSITIVE>
<AMOUNT>${data.amount}</AMOUNT>
</ALLLEDGERENTRIES.LIST>
</VOUCHER>
</TALLYMESSAGE>
</REQUESTDATA>
</IMPORTDATA>
</BODY>
</ENVELOPE>`;
};Step 3: The Webhook Receiver
Now, let's create the Express endpoint that catches the webhook, calls our mapping function, and posts it to Tally.
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const TALLY_URL = 'http://localhost:9000';
app.post('/webhook/new-sale', async (req, res) => {
try {
const saleData = req.body;
console.log("Received new sale JSON:", saleData);
const xmlPayload = mapJsonToTallyXml(saleData);
const tallyResponse = await axios.post(TALLY_URL, xmlPayload, {
headers: { 'Content-Type': 'text/xml' }
});
console.log("Tally Response:", tallyResponse.data);
res.json({ success: true, message: "Sale pushed to Tally successfully." });
} catch (error) {
console.error("Error pushing to Tally:", error.message);
res.status(500).json({ success: false, error: "Failed to push to Tally" });
}
});
app.listen(3000, () => {
console.log('Webhook receiver running on port 3000');
});Important Considerations for Production
- Auto-Creation of Ledgers: What if "Rohan Sharma" does not exist in Tally? The XML above will fail. In production, you must either check if the ledger exists first, or use TDL to force Tally to auto-create the ledger if missing.
- Error Queues: What if Tally is closed when the webhook arrives? The data will be lost. You should use a database or queue (like Redis or RabbitMQ) to store the JSON payloads until Tally successfully processes them.
- GST and Inventory: The simple voucher above is just an accounting voucher. If you need to deduct stock items and calculate IGST/CGST, the XML structure becomes significantly more complex.
Want it Automated Perfectly?
Handling GST calculations, item mapping, and offline queues in XML is difficult. Let our experts build a bulletproof JSON-to-Tally integration for your exact business logic.