Fiscal
Authority-grade sales recording for apps that sell things. Koolbase Fiscal gives every transaction a gapless fiscal number, seals it as an immutable document, delivers it to the tax authority, and keeps the certification the authority grants — the signature and QR code a legal receipt must carry.
What problem this solves
A growing number of countries require every sales invoice to be reported to (or certified by) the tax authority in real time. Getting that right is harder than calling an API: numbers must be gapless and never reused, documents must be immutable once sealed, a network timeout must never produce a duplicate record, and the evidence must survive an audit years later.
Fiscal is the machinery for exactly that. Your app submits a sale; the engine does the rest.
Where it works today
Ghana (gh-gra) — live against GRA's Certified Invoicing System (E-VAT). Invoices, refunds, partial refunds, refund cancellations and purchase records are certified by GRA, and receipts carry GRA's signature and verification QR.
Everywhere else (kb-ref) — the reference adapter runs the full machine without an authority connection: gapless numbering, sealed immutable documents, complete evidence and retention. Useful anywhere e-invoicing isn't mandated yet, and the migration path when it becomes so is a device configuration change, not a rewrite.
The shape of it
Four concepts, and you only interact with the last two directly:
- Merchant — the taxpayer. Their legal name and tax identity.
- Device — a register: one till, one branch, one point of sale. Each device owns its own counter chain, so two tills never collide.
- Intent — one transaction travelling through fiscalization. You create it with
submit()and follow it withstatus(). - Certification — what the authority granted: the signature, receipt number and QR your printed receipt must show.
The intent lifecycle
Every submission lands in one of these states. The distinction that matters most is before versus after a fiscal number is allocated — because an allocated number can never be reused or skipped.
| Status | Meaning | What you do |
|---|---|---|
| blocked | A precondition failed before anything was consumed — no fiscal number was spent. blockedReason says why (e.g. the device has no usable credential). | Fix the cause, resubmit the same clientRef. It resumes. |
| queued / submitting / retrying | Sealed, numbered, and travelling to the authority. isPending is true. | Poll status(). |
| fiscalized | The authority answered and accepted. For certifying document kinds, the certification is present. | Render the receipt. |
| attention | The authority rejected the document, or its credential died after sealing. The register's chain waits behind it — nothing after it is submitted until it's resolved. | An operator resolves it (see your fiscal dashboard / alerts). |
| voided | Terminally closed by an operator without fiscalization. Its number is spent and recorded as void — never reused. | Nothing. It's final. |
Retrying is always safe
You give every submission a clientRef — your own durable reference for the transaction, usually your local sale ID. Submitting the same ref twice always returns the same intent. It never creates a second one, never consumes a second fiscal number.
That single property makes every failure recoverable: if a submit times out, the sale may or may not have fiscalized — and you don't have to know. Submit it again, or poll it. Either way you converge on the truth.
What happens under a network failure
A minimal flow
// 1. Submit the sale. Your own sale id is the clientRef.
final result = await Koolbase.fiscal.submit(
deviceId: registerId,
clientRef: sale.id,
payload: salePayload, // shaped by the device's jurisdiction
);
// 2. Already certified? Print. Otherwise poll.
var intent = result;
while (intent.isPending) {
await Future<void>.delayed(const Duration(seconds: 2));
intent = await Koolbase.fiscal.status(
deviceId: registerId,
clientRef: sale.id,
);
}
// 3. Render the receipt with what the authority granted.
if (intent.isFiscalized) {
final cert = intent.certification; // may be null for some kinds
final qrUrl = cert?['qr_code'] as String?; // Ghana: render as a QR image
}Fiscal and offline
Koolbase's database writes queue transparently when a device is offline. Fiscal submissions deliberately do not. A register needs either a real answer or an explicit “not yet” — silently queueing a fiscalization would leave a cashier believing a receipt is legal when the authority has never seen it.
The pattern for offline-capable point of sale: record the commercial sale locally (the sale is a fact the moment money changes hands), and submit it fiscally when connectivity returns. Because clientRef is idempotent, that replay is a single call with no bookkeeping.