Tax compliance is the worst possible place to let an LLM freewheel, and also one of the better places to put an LLM to work — provided the two roles never get confused. The calculation has to stay deterministic: the tax engine, the rate table, the eTIMS submission logic. What an agent is good at is the noisy job around the edges — reading a batch of invoices and flagging the ones that look wrong before they reach the fiscal device or the VAT return. This is what that split looks like in practice, on Acumatica deployments running eTIMS in Kenya and equivalent fiscalisation regimes elsewhere in East and Southern Africa.
Why tax calculation cannot be an LLM call
An LLM produces the most probable next token, not a verified answer. Tax law is not a distribution — a transaction either qualifies for zero-rating under a specific schedule or it does not, and KRA does not accept "the model was 87% confident" as a defence during an audit. Acumatica's tax engine resolves rate, category, and zone through explicit configuration (tax zones on the customer/vendor, tax categories on the item, and the tax calculation engine that combines them) — the same inputs always produce the same output, and that output is traceable back to the record that drove it. That determinism is the entire point. Any design that routes the actual VAT rate or the actual eTIMS payload through a language model has replaced an auditable rule with an unauditable guess, no matter how good the guess usually is.
Where an agent actually earns its keep
The useful role is triage, not calculation: reading through a batch of AP bills or AR invoices and surfacing the ones that deserve a human look before they post or before they hit the fiscal device. That's a genuinely hard problem for rule-based code alone, because the failure modes are often contextual — a tax zone that's technically valid but wrong for this particular vendor, a description that suggests an exempt supply but carries a standard-rated tax category, a withholding tax certificate that's present but references the wrong engagement. A deterministic report can check "is this field populated" all day; it's weaker at "does this combination make sense given everything else on the document."
In a working setup, the agent has read-only tools into Acumatica — pull the AP bill, pull the linked PO, pull the vendor's tax registration — and a narrow job: return a list of line items with a reason code, nothing else. It doesn't touch the GL, it doesn't call the tax engine, and it never talks to eTIMS. Every flag it raises is confirmed or dismissed by someone who can see the source documents, and the confirmations get logged so the flagging logic can be tuned over time.
This is the single most common miscode I see in eTIMS-connected tenants. Zero-rated supplies (certain exports, some agricultural inputs) still carry a 0% VAT line and appear on the return; exempt supplies (financial services, specific medical items under the VAT Act) don't generate a VAT line at all and are excluded differently. An invoice coded exempt when it should be zero-rated understates output tax reporting even though the cash amount charged to the customer looks identical. This is exactly the kind of error a flagging agent should catch — it can't tell you which one is legally correct, but it can flag "this tax category and this item description don't usually go together" for a human to resolve.
Concrete checks worth running
The checklist that actually catches problems is narrower than it sounds. On documents heading through Acumatica's tax engine and onward to a fiscal device, the recurring issues are:
- Tax zone / tax category mismatch — a customer's tax zone implies one treatment (e.g. export, zero-rated) while the item's tax category implies another, and the resolved rate is the one nobody actually intended.
- Missing tax registration (PIN/TIN) on the counterparty — eTIMS and most East African fiscalisation regimes reject or flag invoices where the buyer's PIN is absent on B2B transactions above the reporting threshold.
- Reverse charge on imported services left unflagged — self-accounted VAT that never shows up because nobody ran the manual journal.
- Withholding VAT/WHT applied to the wrong base — calculated against the gross including VAT instead of the taxable value, a mistake the tax engine won't catch because both numbers are "valid" inputs, just the wrong one.
- Round-tripped credit notes that don't reference the original invoice's fiscal receipt number — technically posts fine in Acumatica, technically breaks the eTIMS credit note requirement.
A tool definition for the flagging agent
The agent doesn't need broad access — it needs a narrow, read-only tool that returns structured facts about a document, plus a prompt that asks it to reason about mismatches rather than recompute anything. A minimal tool definition looks like this:
{
"name": "get_invoice_tax_context",
"description": "Read-only lookup of an AR/AP document's tax-relevant fields for compliance review. Does not calculate or modify tax.",
"input_schema": {
"type": "object",
"properties": {
"document_type": { "type": "string", "enum": ["ARInvoice", "APBill", "ARCreditMemo"] },
"reference_nbr": { "type": "string" }
},
"required": ["document_type", "reference_nbr"]
}
}
// Example return payload the agent reasons over — it never writes back:
{
"customer_vendor_tax_zone": "EXPORT-EA",
"line_items": [
{ "inventory_id": "SVC-CONSULT-01", "tax_category": "STANDARD-16",
"description": "Consulting services - export client" }
],
"counterparty_pin": null,
"fiscal_receipt_ref": null,
"withholding_applicable": true,
"withholding_base_amount": 145000.00,
"document_total_incl_tax": 168200.00
}
Given that payload, a reasonable flag is: tax zone says export but the line item carries a standard 16% category, and the counterparty PIN is null on a B2B invoice that will need to go through eTIMS. Two flags, both worth a human check, neither requiring the agent to know the correct answer — only that the combination looks off.
Closing the loop with a human and the engine
The agent's output is a queue, not a decision. Someone with authority over tax coding reviews each flag against the source document and either corrects the tax category/zone in Acumatica (which re-triggers the deterministic engine) or dismisses the flag with a reason. That dismissal reason is worth capturing even informally — after a few months you'll see which flag types are mostly noise (adjust the prompt or the check) and which are catching real errors before they become a KRA penalty or a rejected eTIMS submission. The calculation stays in code you can unit test; the judgment about "is this combination suspicious" is the part suited to an LLM reading unstructured description fields and comparing them against structured tax metadata.
| Task | Owner | Why |
|---|---|---|
| VAT/WHT rate resolution | Acumatica tax engine | Deterministic, auditable, same inputs always produce same output |
| eTIMS submission payload | Fiscal device integration code | Must match KRA's schema exactly; no tolerance for approximation |
| Anomaly flagging across a batch | Agent (read-only) | Good at spotting inconsistent combinations across free-text and structured fields |
| Confirm/correct flagged items | Human reviewer | Legal and financial accountability stays with a person |
Wrapping up
The pattern that holds up in production is boring on purpose: keep the tax engine deterministic, keep the fiscal device integration deterministic, and let the agent do what LLMs are actually good at — reading messy, high-volume documents and pointing at the ones that don't add up. Give it read-only tools, a narrow prompt, and a human on the other end of every flag, and it earns its keep without ever becoming the thing an auditor has to explain. If you're running eTIMS or another fiscalisation integration and want a second pair of eyes on where automation should and shouldn't touch tax logic, reach out.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.