Rust SDK for integrating with TallyPrime over XML/HTTP.
This crate provides a blocking client for:
- reading masters such as ledgers, groups, stock items, and currencies
- reading vouchers and day-book style voucher ranges
- reading built-in reports such as trial balance, balance sheet, and profit and loss
- creating masters such as ledgers, groups, and stock items
The SDK is built around Tally's XML interface and uses typed Rust models for both inputs and outputs.
This project is working against a live Tally instance and has integration tests for:
- group creation
- ledger creation
- stock item creation
- voucher fetching
- purchase voucher creation using the lower-level XML builder
The high-level client is stable for read flows and master creation. Advanced voucher import flows exist, but some of them are still exposed through lower-level XML builder utilities rather than a dedicated top-level client method.
- Rust 1.75+ recommended
- TallyPrime running with XML over HTTP enabled
- Default endpoint:
http://localhost:9000
If you use a specific company, set it explicitly with TALLY_COMPANY or TallyConfig.current_company. If you do not, the SDK will try to discover the active company loaded in Tally.
Add the crate from crates.io:
[dependencies]
tallyprime-sdk = "0.1"If you want to reference it from Git:
[dependencies]
tallyprime-sdk = { git = "https://github.com/labs-infinitum/tallyprime-sdk" }use tallyprime_sdk::config::TallyConfig;
use tallyprime_sdk::TallyClient;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TallyClient::new(TallyConfig {
host: "localhost".into(),
port: 9000,
current_company: Some("My Company Pvt. Ltd.".into()),
..Default::default()
})?;
client.test_connection()?;
let company = client.active_company_name()?;
println!("Active company: {:?}", company);
let ledgers = client.get_ledgers()?;
println!("Found {} ledgers", ledgers.len());
let vouchers = client.get_vouchers_in_range("20250401", "20250731")?;
println!("Found {} vouchers", vouchers.len());
Ok(())
}The client is configured through TallyConfig:
#[derive(Debug, Clone)]
pub struct TallyConfig {
pub host: String,
pub port: u16,
pub timeout_secs: u64,
pub retry_attempts: u32,
pub current_company: Option<String>,
pub tally_net_account: Option<String>,
pub tally_net_password: Option<String>,
}Defaults:
host = "localhost"port = 9000timeout_secs = 30retry_attempts = 3
Main entry point: TallyClient
Connection and session:
TallyClient::newTallyClient::test_connectionTallyClient::active_company_name
Master reads:
get_ledgers() -> Vec<LedgerSummary>get_groups() -> Vec<GroupSummary>get_stock_items() -> Vec<StockItemSummary>get_currencies() -> Vec<CurrencySummary>
Master writes:
create_ledger(&Ledger) -> ImportResultcreate_group(&Group) -> ImportResultcreate_stock_item(&StockItem) -> ImportResult
Debug variants are also available and print the raw XML request/response:
create_ledger_debugcreate_group_debugcreate_stock_item_debug
Voucher reads:
get_vouchers(from, to) -> Vec<Voucher>get_vouchers_in_range(from, to) -> Vec<Voucher>
Reports:
get_trial_balance(from, to, explode_flag) -> Vec<TrialBalanceEntry>get_balance_sheet(from, to, explode_flag) -> Vec<BalanceSheetEntry>get_profit_and_loss(from, to, explode_flag) -> Vec<ProfitAndLossEntry>get_gst_computation(from, to) -> GstComputationReportget_gstr1(from, to) -> Gstr1Report(voucher-derived; Tally has no builtin HTTPGSTR-1report ID)
Collection reads return typed summaries rather than tuples:
Create/import calls return ImportResult, which includes:
createdaltereddeletedcombinedignorederrorscancelledexceptionslast_voucher_idlast_master_idline_errors
Example:
use tallyprime_sdk::config::TallyConfig;
use tallyprime_sdk::{Group, TallyClient};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TallyClient::new(TallyConfig::default())?;
let result = client.create_group(&Group {
name: "SDK Demo Group".into(),
parent: None,
group_type: None,
alias: None,
basic_group_is_calculable: None,
is_addable: None,
is_subledger: None,
addl_alloc_type: None,
as_original: None,
affects_gross_profit: None,
hsn_applicable_from: None,
hsn_code: None,
hsn_description: None,
hsn_classification_name: None,
hsn_source_of_details: None,
gst_applicable_from: None,
gst_taxability: None,
gst_source_of_details: None,
gst_classification_name: None,
gst_rate_duty_head: None,
gst_rate_valuation_type: None,
gst_rate: None,
gst_state_name: None,
})?;
println!(
"created={}, altered={}, exceptions={}",
result.created, result.altered, result.exceptions
);
Ok(())
}The crate includes runnable examples under examples/:
fetch_all_accounts.rsfetch_all_groups.rsfetch_all_currencies.rsfetch_day_book.rsfetch_trial_balance.rsfetch_balance_sheet.rsfetch_profit_and_loss.rsfetch_gstr1.rscreate_ledger.rscreate_ledger_entry.rs
Run them with:
git clone https://github.com/labs-infinitum/tallyprime-sdk
cd tallyprime-sdk
cargo run --example fetch_all_accounts
cargo run --example fetch_all_groups
cargo run --example fetch_all_currencies
cargo run --example fetch_day_book -- --fy 2025-2026
cargo run --example fetch_day_book -- --from 20250401 --to 20250731 --verbose
cargo run --example fetch_trial_balance -- --fy 2025-2026
cargo run --example fetch_balance_sheet -- --fy 2025-2026
cargo run --example fetch_profit_and_loss -- --fy 2025-2026
cargo run --example fetch_gstr1 -- --fy 2025-2026
cargo run --example create_ledger
cargo run --example create_ledger -- --name "Acme Traders" --parent "Sundry Debtors"
cargo run --example create_ledger_entry -- --party "Acme Traders" --amount 1000 --date 20260701Supported example flags:
--fy YYYY-YYYY--from YYYYMMDD--to YYYYMMDD--verbosefor day book voucher ledger-line output--flatfor report exports withoutEXPLODEFLAG--name,--parent,--opening-balance, and--debugfor ledger creation--party,--account,--amount,--date,--bill-ref,--voucher-number,--voucher-type,--narration, and--debugfor ledger entries
Environment variables used by the examples:
TALLY_HOSTTALLY_PORTTALLY_COMPANY
The SDK expects date inputs in YYYYMMDD format for most public methods.
Example:
2025040120260331
For voucher reads, get_vouchers_in_range applies an exact client-side range filter after parsing the XML response. This exists because Tally's voucher/day book exports are not always consistent about honoring date filters across environments.
For advanced or not-yet-wrapped flows, the crate also exposes the lower-level XML pieces:
That is how the current purchase item-invoice integration test is implemented.
Useful commands:
cargo fmt
cargo check --examples
cargo test --lib
cargo packageUnit tests under src/ run with cargo test / cargo test --lib and do not need Tally.
Integration tests under tests/ require a reachable TallyPrime instance and are marked #[ignore]:
cargo test -- --ignoredSet TALLY_HOST, TALLY_PORT, and optionally TALLY_COMPANY when running ignored tests. If no company is active and TALLY_COMPANY is not set, some flows will skip.
Preferred path: run the Create Release workflow from the Actions tab.
- Configure Trusted Publishing on crates.io for this repo, with workflow
release.yml. - Choose a bump type:
bugfix→x.y.z→x.y.(z+1)minor→x.y.z→x.(y+1).0major→x.y.z→(x+1).0.0
- Create Release updates
Cargo.toml/Cargo.lock, commits tomain, and tagsvX.Y.Z. - It then dispatches the Release workflow, which publishes that tag to crates.io via OIDC (no
CARGO_REGISTRY_TOKENsecret).
You can still publish by pushing a tag yourself:
git tag v0.1.0 && git push origin v0.1.0The Release workflow also supports manual workflow_dispatch for publish-only / dry-run retries.
- The client is blocking today and uses
reqwest::blocking. - Voucher creation is not yet exposed as a single high-level
create_voucher(...)client API. - Some advanced Tally/TDL workflows still require the lower-level XML builder layer.
This repository is licensed under the Apache License 2.0.