The open-source TypeScript SDK for ZIMRA's Fiscalisation Data Management System. Talk directly to FDMS with your own device certificates — no middleware, no per-receipt rent.
Every Zimbabwean POS, invoicing and accounting product has to integrate FDMS. The spec is stateful, cryptographic and unforgiving — this SDK does the hard parts once, in the open.
Canonical signing strings, cents-exact rounding, tax concatenation and the receipt-to-receipt
hash chain — computed and DER-signed for you. Get one byte wrong on your own and FDMS answers
with RCPT020 and no further explanation.
ECDSA P-256 keys, the exact CSR subject ZIMRA requires, activation-key registration and renewal before expiry.
Open, accumulate, close. Counters tracked per tax, currency and money type — then sorted and signed exactly the way the backend verifies them.
Power cuts and dead links don't stop sales. Receipts sign and number at the moment of sale, append to a journal that is never rewritten, and flush in order when connectivity returns, inside FDMS's 72-hour grace window.
The exact data string ZIMRA's validation portal expects, ready to print on every receipt.
Every code carries a colour, a cause, a fix and whether the day can still be closed, plus a
support code a cashier can read out. RCPT030, RCPT012 and friends,
as observed live.
Mutual TLS on plain node:https with one keep-alive agent per device. The fiscal
core itself depends on nothing at all.
The engine has no platform imports: no node:*, no Buffer, no
WebCrypto. Hashing is plain TypeScript. Node, Bun, Deno, browsers, and React Native with the
device key sealed in Android Keystore.
A pending-submit marker goes to disk before every request. Kill the process at any
instruction, restart, call reconcile(): FDMS ends up with every global number
exactly once. Tested by killing the device at every write and every network call.
npx zimra-fdms-simulator is a local FDMS with real mutual TLS that replays the
validation codes seen live. Conformance vectors let a port in any language prove it signs
exactly as FDMS expects.
FDMS signs over cents. Amounts are whole units or cents("11.50"); a float that
might be 11.499999 is refused instead of rounded, so a signature never drifts.
Three calls from code — or three sentences to your agent. The same binary is a Model Context Protocol server built on the stateless MCP 2026-07-28 spec, so Claude Code or any MCP client can run the day through eight fully-typed tools.
The test portal issues device credentials in minutes — no paperwork. One call generates keys, builds ZIMRA's exact CSR and returns your certificate.
Counters, global numbers, hash chain, signature and QR — every receipt fully fiscalised by one method call, or one plain-language sentence over MCP.
Day counters are accumulated, sorted and signed the way FDMS verifies them. No spreadsheet reconciliation at midnight.
Day state lives on disk in the CLI's profile directory, never in the session — open a day from the terminal, sell through the agent, close from either.
import { FiscalDevice } from "zimra-fdms";
const device = new FiscalDevice(identity, credentials);
await device.getConfig(); // tax tables, QR base URL
await device.openDay();
const sale = await device.submitReceipt({
currency: "USD",
invoiceNo: "INV-0001",
lines: [{ name: "Consulting", price: 115, quantity: 1, taxId: 513 }],
payments: [{ moneyType: "Cash", amount: 115 }],
});
// counters, global numbers, hash chain, signature, QR — handled
console.log(sale.qrData);
await device.closeDay(); // signed fiscal-day counters
$ claude mcp add zimra-fdms -- npx zimra-fdms mcp
> open a fiscal day and ring up two loaves of bread, cash
⏺ zimra-fdms · open_fiscal_day
Fiscal day 8 opened.
⏺ zimra-fdms · submit_receipt
Receipt 1 (global no 12) accepted — total 5.00 USD.
QR data ready to print. Hash chain advanced.
// verified live: npm run test:e2e:mcp
Knowledge that cost real fiscal days to earn — shipped as defaults, pinned by a regression suite.
DER
FDMS silently rejects the raw P1363 signatures WebCrypto produces. The SDK converts to DER by default — the difference between "Invoice is valid" and a day of RCPT020.
enum order
BalanceByMoneyType counters sort Cash-before-Card by enum position, not alphabetically. One swapped pair fails the whole day-close signature.
ZIMRA-serial-id₁₀
The certificate request's CN must match ZIMRA's device-name format exactly, with the device ID zero-padded to ten digits.
self-service
No paperwork to start: the FDMS test portal issues device credentials in minutes. Install to ZIMRA-validated invoice in one sitting.
Live fiscalisation still needs ZIMRA onboarding: sample-document approval, live device registration, ITF263, ongoing compliance. Goko Consultancy & Training Services handles that path end-to-end.