orders/paid hits an n8n Shopify Trigger. n8n builds a Tally sales-voucher XML and POSTs it to TallyPrime's HTTP server on port 9000. A nightly Schedule Trigger at 9pm IST tallies the day's orders, payouts, and GST, and sends the owner a WhatsApp summary. The hot-path keeps Tally current; the EOD job is the daily close. Infra cost: ₹520/month.
## Why This Matters Now (August 2025)
Festive season is when monthly book-closing breaks. Order volume triples, COD and prepaid mix shifts, and the gap between Shopify's payout report and Tally's ledger turns into a multi-day reconciliation. Pushing each order into Tally as it's paid means the ledger is never more than a day behind. TallyPrime's XML integration over its built-in HTTP server (port 9000) is the same mechanism Tally has supported for years — stable, documented, no third-party connector needed. We tested this on TallyPrime 4.x with n8n v1.62 in August 2025.
This is the festive-D2C cousin of two flows we've shipped: our Shopify + Razorpay + Tally 4-way Diwali sync and our Shopify + Tally + WhatsApp daily sync. This one leans harder on the webhook hot-path and the owner summary.
## What You'll Need (Prerequisites)
- n8n v1.6x with a public HTTPS URL for the Shopify webhook
- A Shopify store with admin API access (custom app) for the orders scope
- TallyPrime running with its HTTP server enabled on port 9000, reachable from n8n
- A static IP or tunnel to the machine running Tally (it's usually on-prem)
- A WhatsApp Business Cloud account with one approved utility template
- Your Tally company name and the exact sales ledger names, spelled identically
read_orders scope and grab the admin API access token. n8n's Shopify Trigger node uses an Access Token credential plus your shop subdomain.
Tally: No credential object — Tally's HTTP server has no auth. You secure it at the network layer (the tunnel above). In Tally: F1 → Settings → Connectivity → enable "TallyPrime acts as Server", port 9000.
WhatsApp Cloud API: Same as any WhatsApp Cloud build — access token (permanent, via a System User) and phone number ID from Meta's dashboard, into n8n's WhatsApp Business Cloud credential.
## The Trigger Config: Shopify orders/paid Webhook
The Shopify Trigger registers the webhook automatically when the workflow activates. We listen for orders/paid, not orders/create — an unpaid or pending COD order shouldn't post a sales voucher yet.
{
"parameters": {
"topic": "orders/paid"
},
"id": "shopify-trigger-orders-paid",
"name": "Shopify — Order Paid",
"type": "n8n-nodes-base.shopifyTrigger",
"typeVersion": 1,
"position": [240, 300],
"credentials": {
"shopifyApi": { "id": "7", "name": "Shopify store" }
}
}
ENVELOPE containing a HEADER and BODY; the voucher lives inside a TALLYMESSAGE tag, and the import report ID is "Vouchers" per the Tally developer reference. We build the XML in a Code node from the Shopify order, then POST it.
The Code node that assembles the voucher XML:
{
"parameters": {
"jsLanguage": "javaScript",
"jsCode": "const o = $json;\nconst date = new Date(o.created_at).toISOString().slice(0,10).replace(/-/g,'');\nconst amount = Number(o.total_price).toFixed(2);\nconst company = $env.TALLY_COMPANY;\nconst xml = '<ENVELOPE><HEADER><TALLYREQUEST>Import Data</TALLYREQUEST></HEADER><BODY><IMPORTDATA><REQUESTDESC><REPORTNAME>Vouchers</REPORTNAME><STATICVARIABLES><SVCURRENTCOMPANY>' + company + '</SVCURRENTCOMPANY></STATICVARIABLES></REQUESTDESC><REQUESTDATA><TALLYMESSAGE><VOUCHER VCHTYPE=\"Sales\" ACTION=\"Create\"><DATE>' + date + '</DATE><VOUCHERTYPENAME>Sales</VOUCHERTYPENAME><VOUCHERNUMBER>' + o.order_number + '</VOUCHERNUMBER><PARTYLEDGERNAME>Shopify Online Sales</PARTYLEDGERNAME><ALLLEDGERENTRIES.LIST><LEDGERNAME>Shopify Online Sales</LEDGERNAME><ISDEEMEDPOSITIVE>Yes</ISDEEMEDPOSITIVE><AMOUNT>-' + amount + '</AMOUNT></ALLLEDGERENTRIES.LIST><ALLLEDGERENTRIES.LIST><LEDGERNAME>Sales Accounts</LEDGERNAME><ISDEEMEDPOSITIVE>No</ISDEEMEDPOSITIVE><AMOUNT>' + amount + '</AMOUNT></ALLLEDGERENTRIES.LIST></VOUCHER></TALLYMESSAGE></REQUESTDATA></IMPORTDATA></BODY></ENVELOPE>';\nreturn [{ json: { xml: xml, order: o.order_number } }];"
},
"name": "Code — Build Tally Voucher XML",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [460, 300]
}
Then the HTTP Request node POSTs that XML to Tally on port 9000 with content-type text/xml:
{
"parameters": {
"method": "POST",
"url": "http://{{ $env.TALLY_HOST }}:9000",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "Content-Type", "value": "text/xml;charset=utf-8" }
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "text/xml",
"body": "={{ $json.xml }}"
},
"name": "Tally — Post Sales Voucher",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 8000,
"position": [680, 300]
}
0 21 *) pulls the day's paid orders from Shopify, computes gross sales, order count, average order value, and estimated GST, then sends the owner a WhatsApp template. The owner wanted one screen, no login.
{
"parameters": {
"resource": "message",
"operation": "send",
"phoneNumberId": "={{ $env.WA_PHONE_ID }}",
"recipientPhoneNumber": "={{ $env.OWNER_PHONE }}",
"messageType": "template",
"template": "daily_books_summary",
"templateLanguage": "en",
"components": {
"componentValues": [
{ "type": "body", "parameter": "={{ $json.summaryText }}" }
]
}
},
"name": "WhatsApp — Owner EOD Summary",
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1,
"position": [1120, 460],
"credentials": {
"whatsAppApi": { "id": "6", "name": "WhatsApp Cloud" }
}
}
## What Goes in the EOD Owner Summary?
The owner gave us one constraint: it has to fit on a phone screen without scrolling, and it has to mean something at a glance. We landed on six lines, computed in a Code node before the WhatsApp send.
<LINEERROR> inside a 200 body when a ledger name is wrong. We parse the response for <EXCEPTIONS>1</EXCEPTIONS> or LINEERROR and treat it as a failure even though HTTP says OK.
2. Dead-letter failed vouchers. Any order that fails to post goes to a "tally_retry" Google Sheet with the full XML. A 15-minute cron re-attempts. During a festive rush when the Tally machine occasionally hangs, this caught about 30 orders over Diwali week — all re-posted automatically.
3. Reconcile in the EOD job. The 9pm summary counts Shopify paid orders versus vouchers in Tally for the day. If they don't match, the owner's message says so in red, so a gap never hides until month-end.
"Last Diwali, reconciliation took four days in January. This year my books were closed by the time I went to bed each night. The WhatsApp summary is the first thing I check."
LINEERROR inside the XML body — usually because a ledger name in your XML doesn't match Tally exactly. Parse the response for EXCEPTIONS or LINEERROR and copy ledger names character-for-character out of Tally.
### How do you stop duplicate Shopify webhooks double-booking?
Set the Tally voucher number to the Shopify order number. If a duplicate webhook arrives, the second POST carries the same voucher number, and Tally rejects it as a duplicate rather than creating a second sales entry. The voucher is idempotent by design.
### Can this handle GST and multiple tax rates?
Yes, by adding ledger entries for CGST/SGST/IGST in the voucher XML and splitting line items by HSN. For a single-rate catalogue it's one tax ledger pair. For mixed rates you compute the split in the Code node before building the XML. We scope that per client.
### What if the Tally machine is switched off at night?
The hot-path dead-letters any failed voucher to a retry sheet and re-attempts every 15 minutes, so orders posted while Tally was down sync the moment it's back. The 9pm EOD job also flags any Shopify-vs-Tally count mismatch so nothing hides until month-end.
Want your Shopify books closing daily this festive season?
We build the Shopify → Tally → WhatsApp sync, set up the secure tunnel, and self-host it on your infra — for Indian D2C brands in 5–7 working days. Typical project: ₹60k–₹1.1L. Suitable if you do 20+ orders a day on Shopify and run Tally. No slides — just your store and our honest take.
Book a 20-min CallWritten by Hrishikesh Baidya, CTO at Softechinfra. Want this before Diwali? Email contact@softechinfra.com.

