One calculator of import duties and taxes, which runs on its own or inside a portal such as eRegulations. It asks the customs service where the customs service can compute, and computes from the published tariff where it cannot.
In one minute
calculator-bi.eregistrations.dev, calculator-rw.eregistrations.dev, and so on D26.This note answers the request of 28-09-2026, which set three points: take the prototype page as the frontend; extract the ASYCUDA integration from eRegulations and rebuild it as the calculator's backend; make a calculator possible without ASYCUDA. Two agents reviewed it adversarially; their findings are in the review. Words used in a sense of their own are in the dictionary.
Two calculators already call OBR: the prototype and eRegulations v7. Neither can serve a country without ASYCUDA.
The prototype (04-08 to 25-09-2026) is plain HTML and JavaScript for Burundi and Rwanda, behind a small relay that holds the customs key. Its value is what was measured: which fields OBR requires, which declaration types it really computes, the 21 defects found on 06-08-2026. Its debt: Rwanda is a patched copy of Burundi, and there is no store and no cache.
eRegulations v7 carries its own port of the calculator since 04-09-2026. It is bound to eRegulations: its public site does not compile without its administration library.
TariffsBO, 447 lines, talks to ASYCUDA's /api/v1: 3 commodity endpoints, 11 reference tables, the estimate, api-id and api-key headers, the language on every call.TariffsCache, for 7 days by default, filled on the first visit./api/tariffs/*, in eRegulations' shapes, behind a feature switch, limited to 120 requests per minute per address.?embed=true.Other customs systems. Of those checked on 29-09-2026, only ASYCUDA's tariff API computes an estimate on request. Korea's UNI-PASS publishes tariff lookups without one; TANCIS, ICUMS, Webb Fontaine, Kenya's iCMS, GAINDE, CrimsonLogic and SOGET publish no tariff API that could be found. Everywhere else, calculators ingest the tariff and compute.
A page that tells an importer what a shipment will pay, line by line, with the legal source of each line. Never a declaration, never an assessment.
It answers in the country's currency and languages, runs as one installation per country D20, and can be placed inside any portal with one line. Whatever it shows is an estimate; only customs' assessment of a lodged declaration is binding. It does not classify goods: the importer picks the commodity code.
The rule inherited from the prototype. When customs computes, the calculator shows customs' answer and corrects nothing. A defect found in the customs service is reported to it, with an example it can reproduce.
The page speaks one language, the calculator's own. Each provider is translated into it, and no provider is ever asked to change D23.
The contract is the calculator's API: product search, product details, reference tables, estimate. Amounts are exact decimals, each with its currency, and codes are the calculator's. Neither ASYCUDA's shapes nor v7's are kept as they are: one has a single currency per shipment where OBR asked for one per cost element, the other returns results that cannot hold 53 184 493,74 exactly.
Each provider has an adapter that translates, and nowhere else is a provider's vocabulary seen. Where a provider cannot take what the contract carries, its adapter converts with that provider's own published rates, and says so.
Every estimate says which provider answered, which tariff version it used, whether the figure is indicative, and how each line was reached.
| Provider | What it does | Its figures |
|---|---|---|
| ASYCUDA | Sends the shipment to the customs service and shows its answer. A port of v7's TariffsBO |
Customs' own |
| Computing | Computes from a published tariff version: customs value, applicable measures, rates, each tax on its base | Always marked indicative |
| rwandatrade.rw | Asks Rwanda's trade portal, as the prototype does D21 | The portal's |
Each installation chooses its provider on its administration page, not in its environment D20. The template chooses in the environment, so this is code the calculator adds.
When ASYCUDA does not answer, the page says so and shows no figure of ours, unless the country allows it; Burundi does not D11. Exports go through ASYCUDA only, once OBR computes them D15. A stand-in that invents answers exists for demonstrations and tests only; a live installation refuses to start with one, after another country's data was once shown as Burundi's.
Shadow mode runs both providers for one country: the importer sees the authoritative answer, and every difference with the other is logged, tax by tax. For Burundi it uses v7's production settings D24. One call from our server shows whether OBR already accepts its address; OBR is asked only if not. Production calls are real traffic, so the pilot's are few and spread out.
Where no customs service computes, the tariff is stored as data, and the engine computes from it. Nothing a country changes is code.
The model is the EU's TARIC, simplified. It is what the EU, UK and Swiss tariffs use; there is no formal standard.
| Piece | What it says |
|---|---|
| Measure | Which tax applies, to which codes, from which origins, under which procedure, between which dates, under which legal act |
| Component | The rate: a percentage, an amount per unit, or several joined by plus, minimum or maximum |
| Condition | A certificate or a threshold the measure depends on |
| Tax type | Its order, its base, its currency, its rounding: VAT, for example, on customs value plus duty plus excise |
A country is data and parameters. What it needs beyond them, such as a vehicle's cylinder capacity, is a declared rule with its own test, never a branch in a country file. The common external tariff differs by EAC member, through stays, remissions and COMESA preferences, so these are measures of the country that applies them.
Tariffs arrive through connectors, each producing a draft version dated from the day a law takes effect. Pilot 1 needs the first two.
A draft is published only when every declarable code has a customs duty, the analyst has read its differences with the version in force, and the golden cases pass: every tax equal to OBR's after rounding D12, except taxes named in the register of known OBR defects. At least twenty varied golden cases before step 7.2 D25.
Any portal places the calculator with one line. eRegulations v7 becomes one of those portals.
Embedding is an iframe of the country's embedded page, or one script tag that inserts it and adjusts its height. A host may choose the colours, the logo and the language; anything more is a country setting. Only the sites listed for the country may frame it D9.
frame-ancestors built from each country's list of allowed sites.eRegulations v7, in three moves:
/Tariffs show the embedded calculator when a setting names it, and today's page when it does not.Seven steps, each through four gates. Nothing is drawn or built before a step's final agreement.
The gates are: draft, first agreement, adversarial review by agents, final agreement with its date. Every role is the analyst's D8.
| Step | Ends with |
|---|---|
| 1 · Concept note | This page, agreed |
| 2 · Short specifications | One page per module, at most 450 words |
| 3 · Mockups | Public page, embedded page, administration; clickable |
| 4 · Detailed specifications | Contract, engine rules, one sheet per tax, failure policy per provider |
| 5 · Detailed mockups | Every state: empty, error, customs down, shadow |
| 6 · Build phases | One branch and one pull request per phase, each ending with something to click |
| 7 · The tool | 7.1 ASYCUDA provider and the page · 7.2 engine and pilot 1 · 7.3 administration · 7.4 embedding and v7 · 7.5 pilot 2 |
Step 2's eleven modules: 0 public page · 1 product search · 2 estimate form · 3 result and its evidence · 4 ASYCUDA provider · 5 engine · 6 tariff data and ingestion · 7 administration · 8 embedding · 9 health and usage · 10 technical.
The product starts from vertical-base, UNCTAD's template for services built by agents (Next.js, Prisma, Postgres), brought in at step 7 D6, D18. It sets aside what a public calculator does not use, applicant accounts and registration, and the calculator adds the tariff engine, the provider choice and a light page that can be framed.
Each pilot answers one question: does our arithmetic match the customs service's, tax by tax?
Pilot 1, Burundi. The EAC tariff, Burundi's stays and remissions and its national taxes are ingested with their legal articles, computed by the engine, and compared with OBR's own estimate: on stored golden cases, and in shadow mode. The measure of success is how many golden cases match OBR, out of how many.
Pilot 2, Rwanda D13. The same common external tariff with its own stays, remissions and taxes: it proves a second country is data, not code. It is compared with rwandatrade.rw's answers D21.
OBR's requests are done in v7 by other requests, and carried here as requirements D19.
| OBR asked for | Where it lands here |
|---|---|
| All the currencies its system handles | Module 2, reference lists |
| Internal freight added; external freight renamed and explained | Modules 2 and 5; internal freight is outside the customs value |
| One currency per cost element | The contract (§3), module 2 |
| Import and export tabs | Module 2; exports through ASYCUDA only D15 |
| A table of preferential treatments, filtered by origin | Modules 2 and 6; preferences are measures |
| Kirundi off; labels checked; menu position | Module 0 |
| Hosting at OBR | UNCTAD runs the calculator for now, then decided with each authority D22 |
| Risk | What covers it |
|---|---|
| A computed figure taken for an assessment | Every result says "estimate", names its tariff version and the legal act of each line. When one first reaches the public is decided once the calculator is finished |
| Rates go stale: finance laws in July, notices any day (a levy changed in December 2025), weekly exchange rates | Versions dated from any day; exchange rates with their own validity; a warning past a threshold |
| National levies, stays and remissions missing or wrong | The pilots' main test; each carries its legal act; golden cases catch drift |
| OBR's test service silent since 04-09-2026 | Production settings D24 and stored golden cases |
| Public traffic spends the customs quota | Rate limiting, cached reference answers, key custody (§5) |
| The template's method is a first draft | Its origin commit recorded; fixes pulled in with template:pull |
| The embedded page grows heavy | A size budget checked in the build |
Every question of this note is answered. One is deferred: when a figure we compute may reach the public, decided once the calculator is finished.
| Question | Answer | |
|---|---|---|
| ?1 When ASYCUDA is silent, may we show our own figure? | Never, unless the country allows it; Burundi does not | D11 |
| ?2 How close must a golden case be? | Equal to OBR after rounding, tax by tax | D12 |
| ?3 Which country is pilot 2? | Rwanda | D13 |
| ?4, ?8 What does v7 receive while we build? | Nothing from this project; OBR's requests come through other requests | D14, D19 |
| ?5 Exports in the first release? | Through ASYCUDA only | D15 |
| ?6 The repository? | unctad-ai/tariff-calculator-platform |
D16 |
| ?7 Which contract? | Our own; no provider asked to change | D23 |
| ?9 How is a provider chosen? | One installation per country, set on its administration page | D20 |
| ?10 Which OBR host for pilot 1? | Production, with v7's Burundi settings | D24 |
| ?11 Rwanda's source? | The rwandatrade.rw tariff API | D21 |
| ?12 Golden cases before step 7.2? | At least twenty; known OBR defects exempted by name | D25 |
| ?13 When may a computed figure reach the public? | Deferred until the calculator is finished | |
| ?14 Who runs it? | UNCTAD for now, then decided with each authority | D22 |
| ?15 Each country's address? | calculator-<country code>.eregistrations.dev |
D26 |
unctad-ai/tariff-calculator: README.md, server.js, docs/BURUNDI.md, docs/NUEVO-PAIS.md and docs/handover/ at commit 4f0f969, read 28-09-2026; its history from 04-08-2026.docs/handover/2026-09-24-reunion-OBR-CNUCED.md.eRegulations-4.0-Admin (Business/Tariffs.cs, Model/TariffsModel.cs, Model/Country/TariffsCache.cs, commits 7992fe5 and e4fb388), eRegulations-4.0-Public (Api/Presentation/Controllers/TariffsController.cs, AppCode/TariffsRateLimiting.cs, Views/Tariffs/Index.cshtml, wwwroot/assets/js/views/tariffs.js), eRegulations-deploy (releases/7.x, instances/burundi/instance.yml), local copies read 28-09-2026.unctad-ai/vertical-base as of 26-09-2026: README.md, spec/README.md, docs/method/M1, M4 and M5, docs/foundations.md.