efrisapi.com

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:

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.

Internal

$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.

OEM

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:

[email protected]

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.