What Tally XML actually contains, and why it imports cleanly
Short answer: Tally XML is an envelope full of finished vouchers. Not a spreadsheet, not a column mapping — each entry already carries its date, voucher type, narration and ledger postings in the structure Tally expects, which is why the import needs no TDL and no template.
If you are just trying to get a statement in, the four-step path is on bank statement to Tally. This post is for when you want to know what is in the file.
The envelope
Every Tally import file has the same outer shape:
<ENVELOPE>
<HEADER>
<TALLYREQUEST>Import Data</TALLYREQUEST>
</HEADER>
<BODY>
<IMPORTDATA>
<REQUESTDESC>
<REPORTNAME>Vouchers</REPORTNAME>
</REQUESTDESC>
<REQUESTDATA>
<!-- one TALLYMESSAGE per voucher -->
</REQUESTDATA>
</IMPORTDATA>
</BODY>
</ENVELOPE>
REPORTNAME is what tells Tally what kind of data is arriving — Vouchers here, but the same
envelope carries masters like ledgers with a different report name. If you want the file to target
a specific company rather than whichever one is open, a STATICVARIABLES block with
SVCURRENTCOMPANY goes inside REQUESTDESC.
One voucher
Here is a single payment, annotated:
<TALLYMESSAGE xmlns:UDF="TallyUDF">
<VOUCHER REMOTEID="…" VCHTYPE="Payment" ACTION="Create"
OBJVIEW="Accounting Voucher View">
<DATE>20260704</DATE>
<VOUCHERTYPENAME>Payment</VOUCHERTYPENAME>
<VOUCHERNUMBER>1</VOUCHERNUMBER>
<NARRATION>UPI/DR/451203/GROCERY [Ref: 451203]</NARRATION>
<ALLLEDGERENTRIES.LIST>
<LEDGERNAME>Suspense</LEDGERNAME>
<ISDEEMEDPOSITIVE>Yes</ISDEEMEDPOSITIVE>
<AMOUNT>-1240.00</AMOUNT>
</ALLLEDGERENTRIES.LIST>
<ALLLEDGERENTRIES.LIST>
<LEDGERNAME>HDFC Bank</LEDGERNAME>
<ISDEEMEDPOSITIVE>No</ISDEEMEDPOSITIVE>
<AMOUNT>1240.00</AMOUNT>
</ALLLEDGERENTRIES.LIST>
</VOUCHER>
</TALLYMESSAGE>
Four things in there are worth understanding, because each one is a way the file can fail.
The date has no separators
20260704 is 4 July 2026. YYYYMMDD, no dashes, no slashes. Tally displays dates as 4-Jul-26
everywhere in its interface and accepts none of that here. This catches almost everyone building a
file by hand the first time.
Debits are negative
Tally’s sign convention reads backwards until it clicks:
| Entry | ISDEEMEDPOSITIVE |
AMOUNT |
|---|---|---|
| Debit | Yes |
negative |
| Credit | No |
positive |
The two entries in a simple voucher therefore sum to zero, which is the property Tally checks. A bank Receipt debits the bank ledger; a Payment credits it. Get the pairing backwards and the voucher either fails or posts inverted — which is worse, because it imports.
Ledger names must already exist, exactly
<LEDGERNAME>HDFC Bank</LEDGERNAME> is matched as a string against the ledgers in your company.
HDFC Bank Ltd, HDFC bank and HDFC Bank with two spaces are three different ledgers as far as
this match is concerned. Create the ledger first, then copy the name out of Tally rather than
typing it from memory.
This is why the export asks for the bank ledger name up front, and why everything else lands
against a contra ledger — Suspense by default — for you to reallocate inside Tally.
REMOTEID is what stops duplicates
REMOTEID is a stable external identifier for the voucher. Tally uses it to recognise that an
incoming voucher is one it already has, so a second import updates rather than adds.
It matters more than it sounds. Convert January–March, then later convert February–April, and the overlap is two months of transactions present in both files. Without stable IDs you now have two copies of each. TieOut derives the ID by hashing the transaction’s own fields — bank, date, reference, debit, credit and position — so the same transaction always produces the same ID, and re-importing is safe.
Not every converter does this. With an unfamiliar tool, test on a scratch company before running it against live books.
What is deliberately not in the file
Ledger creation. The XML posts to ledgers; it does not create them. That is a separate import
with a different REPORTNAME, and doing it automatically would mean a converter inventing chart-of-
accounts structure, which is your decision and not a parser’s.
Categorisation. Every transaction lands against the same contra ledger. Allocating them is
ordinary Tally work and genuinely needs judgement — a narration reading NEFT DR 4471 SHARMA could
be a supplier payment, a salary or a loan repayment, and only you know which.
GST fields. These are bank vouchers, not invoices; ISINVOICE is No and there is no GST
block.
Before you import, check the extraction
The XML being well-formed says nothing about whether it contains every transaction. Tally will
happily import a clean file that is missing a page. The check that catches that is arithmetic —
every row satisfying previous balance + credit − debit = current balance, with the last balance
matching the printed closing figure — and it runs on the statement before the XML is ever written.
That is how the balance check works, including what it cannot catch.
Once the extraction is verified, the import path itself is four steps: converting and importing a bank statement into Tally. And if you are wondering whether you need a TDL for any of this, the answer is usually no — TDL versus XML import.
FAQ
What is Tally XML?
It is Tally's native import format — an XML envelope containing finished vouchers, each with a date, a voucher type, a narration and a list of ledger entries that sum to zero. Tally reads it directly from Gateway of Tally → Import → Vouchers, with no add-on or column mapping required.
What date format does Tally XML use?
YYYYMMDD with no separators, so 4 July 2026 is 20260704. This is one of the more common causes of a hand-built XML file being rejected, because it does not match the date format Tally displays anywhere in its own interface.
Why do Tally ledger amounts have a minus sign?
Tally's convention is that a debit entry carries a negative AMOUNT with ISDEEMEDPOSITIVE set to Yes, and a credit entry a positive AMOUNT with ISDEEMEDPOSITIVE set to No. The two entries in a voucher therefore sum to zero. It looks inverted the first time you see it, and it is the second most common reason a hand-built voucher fails to import.
How do I stop a Tally import creating duplicate vouchers?
Give each voucher a stable REMOTEID — an identifier derived from the transaction itself, so the same transaction always produces the same ID. Tally then treats a second import of the same voucher as an update rather than a new entry. Without it, re-importing an overlapping date range duplicates every transaction in the overlap.
Can I write Tally XML by hand?
For a handful of vouchers, yes. For a bank statement it is not worth it — the date format, the sign convention and the ledger-name matching all have to be exactly right for every row, and a single malformed voucher can cause Tally to reject silently. A converter that emits the format is the shorter path.