Skip to main content

ADR-0034: Multi-Country Registry Architecture

Status: Accepted Date: 2026-04-06

Context

ADR-0019 defined the agent pipeline architecture with country routing at the agent layer. This ADR documents the service layer beneath it -- the per-country registry implementations that connect to government APIs and public data sources across 12 European countries.

Each European country operates its own company registry with incompatible APIs, data models, authentication schemes, and rate limits. Belgium's KBO uses a SOAP/XML interface, France's INPI uses OAuth2 + REST, Czech Republic's ARES uses XML with custom namespaces, the Netherlands' KVK requires API key authentication, and Romania's ANAF exposes a public REST API with no authentication but returns Romanian-language responses. Some countries (Estonia, Denmark) offer modern REST APIs; others (Slovakia) require scraping court register HTML.

A universal adapter pattern would force lowest-common-denominator normalization, losing country-specific data that is available from some registries but not others (e.g., Belgian gazette publications, French BODACC legal announcements, Czech insolvency records). The system needs per-country specialization while maintaining a consistent interface for the agent layer.

Decision

Implement one service file per country (or per registry within a country) under app/services/registries/, with a country routing layer that maps ISO country codes to the appropriate service(s).

Current registry coverage (12 countries, 21 service files):

CountryServicesAPI Type
BEKBO (via existing kbo_service.py, nbb_service.py)SOAP/REST
CZARES, Justice.cz, ISIR (insolvency)XML REST
CHZefixREST
DKCVR (Virk)REST
EEAriregister (e-Business Register)REST
FIYTJ (Business Information System)REST
FRINPI, INSEE, BODACCOAuth2 REST
NLKVK, KVK Jaarrekeningen (annual reports)REST
NOBrreg, Regnskapsregisteret (accounting register)REST
ROANAF (company identity + financial), ONRC (directors via BrightData), NomenclatorREST + scraping
SKORSR (commercial register), RUZ (financial statements)Scraping + REST

Each service file is self-contained: it handles its own HTTP client setup, authentication, response parsing, error handling, and data normalization. Services return Pydantic models that the registry agent consumes. A shared financial_utils.py provides common financial data normalization (currency conversion, accounting period detection).

Key design choice: per-country files rather than a generic adapter because:

  • Government APIs change independently and unpredictably -- isolating changes to one file prevents regressions in other countries
  • Each country has unique data available (Belgian gazette publications, Czech insolvency records, Norwegian accounting data) that a generic adapter would discard
  • Testing is country-scoped -- a change to the ARES parser can be tested without running Belgian or French tests

Consequences

Positive

  • Adding a new country is an isolated task -- create a service file, add the route, write tests -- no changes to existing countries
  • Country-specific data (insolvency records, gazette publications, financial statements) is preserved and available to downstream agents
  • Each service file can be independently rate-limited, circuit-broken (ADR-0032), and monitored
  • 12-country coverage provides broad EU reach for the initial sales pipeline

Negative

  • 21 service files is significant maintenance surface -- government API changes require per-country updates
  • No shared base class enforces a consistent interface -- each service returns its own response shape, with normalization happening in the agent layer
  • Some countries have incomplete coverage (e.g., Romania directors require BrightData scraping because ONRC has no public API for directors)

Risks

  • Government API deprecation with no advance notice -- several registries (particularly Eastern European ones) have changed endpoints without versioning
  • Scraping-based services (SK ORSR, RO ONRC via BrightData) are fragile against HTML structure changes
  • Rate limit differences across countries could cause circuit breaker settings (ADR-0032) to be suboptimal for some registries