EFRIS integration that already knows where the spec is wrong.
A Python client for Uganda Revenue Authority's EFRIS, covering all ~70 interfaces — invoicing, credit notes, goods, stock, fuel/EDC. You get the source.
Pricing & licensing Read the error-code reference
Stuck on a URA rejection right now? The error-code reference is free and always will be — 1345, 2253, 676, 1304 and the rest, with what each one actually means.
Transcribing the spec is the easy part
Turning URA's PDF into seventy endpoint methods is a weekend of tedium, and any competent developer — or a decent coding model — can do it. That is not what this is.
A faithful implementation of the documentation does not work. The specification is wrong in specific places, and the service tells you so only with a number. These are the places:
- The cipher is AES-128-ECB — the one mode every security guide says never to use. A careful implementer reaches for CBC and loses days to invalid signature.
- Signing is SHA1, not SHA256.
- The signature covers the transmitted ciphertext, not the plaintext JSON.
-
The key field in the handshake response is spelled
passowrdDes, and the key it carries is base64-encoded twice. -
summary.taxAmountmust include excise whilesummary.grossAmountmust exclude it — two rules pulling in opposite directions, and each has its own error number. - URA documents T119 as encrypted. The live service accepts it unencrypted.
Every one of those cost real time to find, against a sandbox that answers with a bare code. They are documented in full in the error reference, and already handled in the code.
What using it looks like
from efris import EfrisClient, EfrisConfig
client = EfrisClient(EfrisConfig(
tin="1000000000",
cert_path="/secure/certs/1000000000.pfx",
cert_password=os.environ["EFRIS_CERT_PASSWORD"],
test_mode=True,
))
client.get_server_time() # proves connectivity before crypto
result = client.upload_invoice(document) # T109 — fiscalised
The T104 handshake, session-key caching, encryption and response decoding happen underneath. Failures raise with URA's own code, so you branch on the number instead of parsing prose:
try:
client.upload_invoice(document)
except EfrisError as exc:
if exc.code == "2253": # duplicate seller reference
...
Built for multi-tenant use
Nothing is global. A client is bound to one configuration and holds its own session key, so an ERP serving many taxpayers keeps one client per tenant and they never share cryptographic state. Credentials come from wherever you keep them — a database row, a secrets manager, per-tenant files.
registry.for_tenant("acme").upload_invoice(document)
~70 interfaces
T101–T187. Invoicing, credit notes, goods, stock, reference data, fuel/EDC.
Two dependencies
requests and cryptography. No
framework lock-in.
REST service included
Optional FastAPI wrapper for callers that aren't Python.
Pricing
Most Ugandan businesses pay around 1,000,000 UGX per year for EFRIS API access, regardless of provider. This is the buy-instead-of-rent option: you get the source, and you stop renewing.
$1,000 one-time
3,700,000 UGX · roughly 3.5 years of a subscription.
- Full source, modify freely
- Use for your own TINs, unlimited
- Multi-tenancy included
Updates & new error fixes: $220/year (800,000 UGX), optional.
from $3,500 one-time
13,000,000 UGX · for ERP vendors shipping to their own customers.
- Everything in Internal
- Embed and redistribute in your product
- Unlimited end-user deployments
Updates & new error fixes: $800/year (3,000,000 UGX), optional.
Payable in USD or UGX. UGX figures are indicative and settled at the rate on the invoice date. Integration support and done-for-you setup are quoted separately.
Why the annual renewal is the part that matters. URA keeps moving — T186 and T187 arrived in 2024, T127 and T130 changed in 2025. A library bought today is stale within a year without updates. The subscription is also where new rejection fixes land, so it compounds: every URA error solved makes it further ahead of a fresh reimplementation.
Get in touch
Licensing questions, a demo against the sandbox, or help with an integration that is already stuck:
Independent software. Not affiliated with, endorsed by, or operated by the Uganda Revenue Authority. "EFRIS" refers to URA's system, which this library integrates with.