We use cookies
We use cookies and similar technologies to improve your experience, analyse traffic, and personalise content. You can accept all cookies or reject non-essential ones.
With the bridge running, every sales invoice you save in TallyPrime can reach your customer minutes later: on WhatsApp or email with a branded PDF, or as an SMS carrying the link. The same records keep your customer and supplier lists current, put your sales, purchase and stock figures in front of your dashboards, drive follow-up on the customers who still carry a balance, and fill a portal in your own brand where each customer sees their own invoices and the balance outstanding on their account. This guide is where you decide all of it: which companies and kinds of Tally record — sales invoices, purchase bills, customers, suppliers, bank accounts, stock items — are shared, where each goes, how often the bridge looks, and what your flow, the automation you build in SurveyAnalytica, does with them. If the bridge is not on the PC yet, see the Tally Prime Connector — Installation Guide.
All of it lives in one text file, bridge.yml, beside sa-tally-bridge.exe — usually in C:\ProgramData\SurveyAnalytica. Right-click it, choose Open with, then Notepad. It is plain text in YAML, where the indentation carries meaning.
Indent with spaces only, never the Tab key, and keep the indentation of lines you copy from this guide. The bridge starts from a file it can read end to end; if an indent slips it names the line to correct and waits for the corrected file. Check your edit before leaving it to Windows — one command does it.
In the folder holding bridge.yml, click in File Explorer’s address bar, type cmd, press Enter, then run sa-tally-bridge.exe run --config bridge.yml. If it prints its schedule and keeps running, the file is good: press Ctrl+C, then restart the bridge.
The bridge reads bridge.yml at start, so a saved change takes effect at the next start. If it is still running in a Command Prompt window, press Ctrl+C there and run it again. Once it starts by itself (Step 7 of the Installation Guide), restart it from Windows Task Scheduler: open Task Scheduler Library → SurveyAnalytica → Tally Bridge, right-click the task and choose End, then right-click it again and choose Run. Signing out and back in does the same: the task starts at logon.
Use the wizard, sa-tally-bridge.exe init, for first-time setup. It starts from a clean sheet each time, so make later changes in Notepad. Keep each company under one key — the name the wizard wrote, or its folder number — and keep that key: it is where the bridge holds that company’s place, so each run reads only what is new and each record arrives once.
The companies: block lists the companies to sync, one entry each, so several companies or branches run from the one Tally PC. Each has a key of your choosing, which arrives with every record as companyCode; a name spelled as Tally spells it; and a transactions: block naming the kinds of record to read.
bridge.yml — the companies: block
companies:
"010001":
name: "Acme Traders"
transactions:
sales: {}
purchase: {}
ledgers/debtors: {}
stockItems: {}
"010002":
name: "Globex Industries"
transactions:
sales: {}
ledgers/debtors: {}
Every example here is a piece of the same file: global: and companies: appear once each, so add only the lines you are missing, at the same indentation. The empty braces are deliberate: a kind of record needs settings only when it has its own destination, which is the next section. The seven kinds, called streams:
| Write this | What it sends |
|---|---|
sales | Sales invoices, your own voucher types included: the bridge asks Tally what each type is based on, so GST SALES is filed as a sale. |
purchase | Purchase bills, custom voucher types handled the same way. |
ledgers/debtors | Customers — every ledger under Sundry Debtors, sub-groups included. |
ledgers/creditors | Suppliers — every ledger under Sundry Creditors. |
ledgers/bank | Bank accounts. |
ledgers | The whole chart of accounts — thousands of rows. For customers or suppliers, the scoped lists above are sharper. |
stockItems | Stock items, with closing quantity, value and rate. |
Customer and supplier records carry the party’s details as Tally holds them — mailing name, email, phone, mobile, GSTIN, income-tax number, address and closing balance — which keeps a contact list current without re-typing. Sales and purchase records carry the whole voucher: header, stock lines, ledger and tax lines, narration, and the GST e-invoice details (IRN, acknowledgement number and date) when Tally has them.
Sales invoices and purchase bills, your own voucher types included; customers, suppliers, bank accounts and, if you want it, the full chart of accounts; stock items. Other voucher types — receipts, payments, contra, journals, credit and debit notes, orders and delivery notes — are outside the connector’s scope today.
Every record goes to a flow whose trigger is a Webhook — the one you built in Step 3 of the Installation Guide, and as many more as you like, each built the same way. That panel shows the two values you copy into bridge.yml: the rows labelled URL : and KEY :, each with a copy button. The URL line is named for where it sits: webhook_url under global:, under a company and under a kind of record; url inside transaction_webhooks: and heartbeat:, with the key as api_key beside it. Copy the shape from the example below, using the name that belongs to the level you are editing — that is the spelling the bridge reads. Records with no destination yet wait safely in the queue until one is set.
Good to know. Copy the URL exactly as the panel shows it; if your office firewall restricts outgoing traffic, allow outbound HTTPS to the host name at its start. The key travels in a header named code, and it is all the flow needs: leave the HMAC Signature (Optional) boxes empty.
Send everything to one flow, or give a company or a kind of record its own.
bridge.yml — parts of the global: and companies: blocks
global:
webhook_url: "PASTE-THE-URL-FROM-THE-TRIGGER-PANEL"
api_key: "PASTE-THE-KEY-FROM-THE-TRIGGER-PANEL"
transaction_webhooks:
sales:
url: "PASTE-THE-SALES-FLOW-URL"
api_key: "PASTE-THE-SALES-FLOW-KEY"
ledgers/debtors:
url: "PASTE-THE-CONTACTS-FLOW-URL"
api_key: "PASTE-THE-CONTACTS-FLOW-KEY"
companies:
"010002":
name: "Globex Industries"
webhook_url: "PASTE-THE-GLOBEX-FLOW-URL"
api_key: "PASTE-THE-GLOBEX-FLOW-KEY"
transactions:
sales: {}
Records travel in groups, one company and one kind of record to a group. For every group the bridge takes the first destination filled in: this company and this kind of record, then this company, then this kind of record, then the global fallback. The key travels with the URL it was written beside, so each flow is reached with its own key.
Tip. Generate New, under the key, issues a fresh KEY; the running flow keeps the present one until somebody publishes it. After using it, paste the new KEY into bridge.yml, restart the bridge, then press Update Live. (A live flow shows Update Live; one never switched on shows Activate.) Anything sent meanwhile waits in the queue.
The bridge reads Tally either automatically, every few minutes, or on a schedule, at clock times you choose — set in the sync block under global. With no sync block it is automatic, every five minutes.
Reads at a fixed interval — five minutes unless you say otherwise — and once as soon as it starts, so invoices reach your flow within minutes of being entered and your customer the same day.
Reads only at the times you give, such as 21:30 every night. In between it makes no requests to Tally at all, so the working day belongs to your staff.
bridge.yml — inside the global: block, automatic
global:
sync:
mode: interval
every: 5m
bridge.yml — inside the global: block, scheduled
global:
sync:
mode: cron
cron: "30 21 * * *"
timezone: "Asia/Kolkata"
| Setting | Default | What it does |
|---|---|---|
mode | interval | Either interval or cron. A cron line with no mode is read as cron. |
every | 5m | How often to read Tally in automatic mode: 30s, 5m, 2h. |
cron | none | The clock times in scheduled mode. Five parts: minute, hour, day of month, month, day of week. |
timezone | UTC | The zone the cron times are read in, as a place name such as Asia/Kolkata. Set it, so 21:30 is 21:30 at home. |
run_on_start | true | Read once as soon as the bridge starts. In scheduled mode, only on a PC yet to complete a run. |
catch_up | true | If a scheduled time passed while the PC was off, do one run when it comes back. |
catch_up_grace | 30m | How far behind a missed time must be for a catch-up run to be worth doing. |
retry_every | 15m | How often to re-try whatever is still to be read this cycle. |
first_run_guard | 0 (off) | Holds back the first-ever sync of a company with more changes than this number, in either mode, so a full-history read never starts on an unattended night; that kind of record reads needs_backfill until you set this back to 0 and let it run. |
| Write this | It means |
|---|---|
"30 21 * * *" | Every day at 21:30. With timezone: "Asia/Kolkata", 21:30 IST. |
"0 19 * * 1-5" | Monday to Friday at 19:00, nothing at the weekend. |
"0 */2 * * *" | Every two hours, on the hour. |
Write five parts, not six: a leading seconds part would turn "0 30 21 * * *" into half past every hour. The bridge checks the expression before it starts, printing global.sync.cron must have exactly 5 fields (minute hour day-of-month month day-of-week), got 6 in "0 30 21 * * *" in a Command Prompt window — so check a new schedule by hand, as above.
A scheduled time starts a cycle. If a company was not ready — its login prompt still on the desktop, or Tally not yet answering — the bridge keeps it on the list and tries again every retry_every, a quarter of an hour by default, until it succeeds or the next scheduled time comes round: a company opened at 09:40 the next morning syncs at 09:40.
If the PC was off over a scheduled time, one catch-up run covers the gap: three missed nights cost one run, because the bridge asks for everything changed since it last looked. In scheduled mode a restart keeps the appointment rather than bringing it forward; for figures now, switch to automatic for the day.
This is where Tally records become the thing you wanted: the invoice sent on WhatsApp or email with a branded PDF, or as an SMS carrying the link; customer and supplier lists in step with Sundry Debtors and Creditors; a live register of sales in Google Sheets or Excel; a feedback survey once the invoice has gone out; reminders on the days you choose, sent by a scheduled flow over a contact list of the customers who still carry a balance; figures for dashboards and analysis; and a branded portal where customers see their own invoices and the balance outstanding on their account.
sa-tally-bridge.exe test --config bridge.yml. It sends about ten recent records per kind of record, straight away.records. Everything below now runs once per record, not once per group.Why this order. The bridge moves forward from what it has delivered, so have the flow live before the big first load. Until it is active, a flow keeps what arrives as a test sample — which is what steps 2 and 3 need.
Tip. Use one route per company — the bridge, or the cloud-side Tally data source on a Schedule trigger, not both — so each record arrives once.
| Action | Loop needed? |
|---|---|
| WhatsApp, SMS, Slack, Microsoft Teams, social posts | Yes. One message at a time. |
| Google Sheets, Excel | Yes. One row per record. |
| Contact list, Planner, Survey response, Org user, Shopify, Salesforce, Mailchimp | Yes. Each writes a single record. |
| No. A template can repeat a list inside itself — several invoices in one statement; a loop gives one email per invoice. | |
| Create Document, Task | No. Both work through a list, so one document can cover a whole group. |
Where a loop is needed, the step says so and offers an Add Loop button.
Each request is one of those groups — a batch, from one company and one kind of record — wrapped in six fields.
| Field | What it holds |
|---|---|
companyCode | The key you used under companies:. |
companyName | The company’s name as Tally spells it. |
transactionType | The kind of record: sales, ledgers/debtors, and so on. |
batchStart, batchTotal | Where this batch starts within everything found this cycle (0, then 250, then 500), and how many were found. |
records | The records themselves, up to 250 per request — the array your loop iterates. |
Inside records, the field names are Tally’s own, in capitals, and your user-defined fields keep their plain names. Every value is text, amounts and dates included, and a field empty in Tally is left out of that record — so build each step on the fields you picked in step 7.
One request, shortened
{
"companyCode": "010001",
"companyName": "Acme Traders",
"transactionType": "sales",
"batchStart": 0,
"batchTotal": 312,
"records": [
{
"GUID": "0a1b2c3d-0000-4000-8000-000000000001-00000101",
"REMOTEID": "0a1b2c3d-0000-4000-8000-000000000001-00000101",
"DATE": "20250401",
"VOUCHERNUMBER": "101",
"VOUCHERTYPENAME": "GST SALES",
"PARTYLEDGERNAME": "Example Customer Pvt Ltd",
"AMOUNT": "5900.00",
"PARTYGSTIN": "29XXXXXXXXXX1Z5",
"ALLINVENTORYENTRIES": [
{
"STOCKITEMNAME": "Sample Item A",
"BILLEDQTY": "10 Nos.",
"RATE": "500.00/Nos.",
"AMOUNT": "5000.00"
}
]
}
]
}
The field to build on is GUID — Tally’s permanent identifier for that voucher or ledger — with REMOTEID as its stand-in, since some vouchers carry it under that name. When somebody edits an invoice, when its e-invoice details arrive later, or when a reply does not reach the bridge, the record is sent again under the same identifier. Match on it and update rather than add: that keeps one invoice one row, in a sheet, a dashboard or the portal.
You choose which companies and kinds of record are shared. For those, the full voucher or ledger travels over an encrypted connection: party and mailing names, GSTIN, PAN, postal address, email, phone and mobile numbers, item names, rates, tax amounts and balances. Most firms share ledgers/debtors and ledgers/creditors rather than the whole chart of accounts, which keeps what leaves the PC to the parties you actually trade with.
Records travel when something has changed, so a quiet night is quiet by design. For positive confirmation, the status report posts a summary to a flow of its own after every run, with that flow’s URL and KEY. Add it by hand:
bridge.yml — inside the global: block
global:
heartbeat:
url: "PASTE-THE-STATUS-FLOW-URL"
api_key: "PASTE-THE-STATUS-FLOW-KEY"
Each report carries outcome (success, partial or blocked), blockedReason (gateway_error, gateway_empty, no_companies), outboxDepth, recordsEnqueued, pendingStreams, nextFireAt and hostname. The most useful alert: on a nightly schedule, raise a flag if no report has arrived from a machine for 26 hours.
On the Tally PC, open a Command Prompt in that folder, as above, and run:
Command Prompt
sa-tally-bridge.exe status --config bridge.yml
What it prints
sa-tally-bridge [version] on TALLY-PC
Schedule : cron "30 21 * * *" (Asia/Kolkata)
Last run : Sat, 15 Aug 2026 21:30:04 IST → 21:34:11 (partial)
Next run : Sat, 15 Aug 2026 21:45:04 IST
Records : 312 enqueued last run; outbox depth 0
Pending : 1 stream(s) awaiting retry
- Globex Industries / sales : company_closed
ok Acme Traders / sales : synced (watermark 260321/260409, 312 records)
ok Acme Traders / stockItems : up_to_date (watermark 8821/8821, 0 records)
A Blocked : line appears between Last run and Next run when a whole run was blocked. outbox depth counts batches still to be delivered; 0 is what you want. watermark 260321/260409 is the bookmark in plain numbers: the change number read up to, then the company’s change number now — when they match, everything is in. status reports the last completed run.
| Status | Meaning | Tried again? |
|---|---|---|
synced | New or changed records were read and queued for sending. | Done |
up_to_date | Tally answered and nothing has changed: the healthy resting state. | Done |
company_closed | That company is not open in TallyPrime, usually a login prompt waiting on the desktop. | Yes |
type_map_failed | The list of voucher types was not read this time, so the bridge held that stream back rather than file your custom types under the wrong heading. | Yes |
no_counter | The company reported no change counter this time. | Yes |
needs_backfill | A first-ever sync held back by first_run_guard. Listed by sa-tally-bridge.exe status --config bridge.yml --json. | No |
error | An attempt did not complete; the reason is recorded and the bookmark stayed put, so those records come next time. | Yes |
status prints..1 to .5. The first thing to send support.| What you see | What it means | What to do |
|---|---|---|
| Everything delivered, and the flow has yet to act. | The flow is saved but not activated, or paused, so what has arrived so far is kept as its test sample. | Open the flow and press Activate. The next sync is acted on in full. |
outbox depth keeps rising. | The flow has yet to accept those batches: a KEY that has moved on, a URL from another flow, or a kind of record added by hand with no destination yet. | Copy URL : and KEY : from the panel again and restart the bridge; check that the kind of record has its own URL, or that webhook_url under global: is filled in. Everything waits safely in the queue meanwhile. |
One company always reads company_closed. | It is not open in TallyPrime, usually because its login prompt is waiting. | Open that company in the Tally window and type its password there. The bridge picks it up at the next retry. |
Blocked : gateway_error or gateway_empty, with every company company_closed. | Tally is not answering yet, or answered with no company open; the Blocked line is the one to read. (gateway is the bridge’s own word for Tally’s port-9000 connection.) | Look at the Tally window: start Tally, or clear the prompt waiting there. |
companyName or batchTotal blank below the loop. | The loop’s Include parent fields in each entry switch is off. | Open the loop step and turn it on. |
| Deliveries stopped after somebody pressed Generate New. | A new KEY was issued and the flow published, so the flow now expects that KEY. | Copy the new KEY into bridge.yml, restart the bridge, then press Update Live. |
| No records since the PC was restarted. | The bridge runs in your signed-in Windows session, and Tally needs its companies open. | Sign in and open the companies; the bridge resumes about a minute later. On a PC nobody sits at, turn on Windows auto-logon and it comes back by itself. |
| Records arrive twice. | The older Tally add-on is still loaded; or the company is also a Tally data source on a Schedule trigger; or it is listed twice under two keys. | Keep the one route you want, remove the other, restart the bridge. |
synced with Records : 0. | What changed this cycle were voucher types outside the connector’s scope, such as receipts. | Nothing to do: sales and purchase records are up to date. |
Reliability, in three lines.
Recovery point (RPO): effectively zero. Tally stays your system of record, and a company’s bookmark moves only once those records are safely in the queue on disk — so whatever was entered in the companies and kinds of record you selected, while the internet, the PC or the bridge was away, is picked up on the next run, nothing re-keyed.
Recovery time (RTO): decided by your PC. There is no recovery procedure: once Windows is signed in and Tally has its companies open, the bridge resumes by itself about a minute later, catches up any scheduled time that passed, and re-tries every 15 minutes any company that was not open.
Availability: Windows starts it at sign-in, and starts it again — three attempts, a minute apart — if it ever stops. Fuller picture: Reliability at a glance.
sa-tally-bridge.exe test as often as you like: it moves no bookmark and changes nothing you have already synced.HTTPS_PROXY for the Windows account the bridge runs under — say http://proxy.example.local:8080 — and restart it.up_to_date.