{"openapi":"3.1.0","info":{"title":"registry-mcp","description":"The company registry MCP: company data for AI agents, any country. One JSON shape, many national business registries — a `CompanyReport` from this REST API is byte-identical to the one the MCP tools return.\n\nTwo countries answer today. **Norway** (`/v1/NO/…`) — Enhetsregisteret (Brønnøysundregistrene, slug `brreg`), looked up by organisasjonsnummer (orgnr, org.nr), with MVA/VAT registration status. **United Kingdom** (`/v1/GB/…`) — Companies House, looked up by company number (company registration number, CRN) such as `00445790`, with annual accounts and confirmation statement deadlines. The code is `GB`; `/v1/UK/…` is a `404 unsupported_country`. `GET /v1/countries` is the live list and names any registry that needs a credential (`requires_api_key`, `api_key_env`).\n\nAlso searchable as: brreg, brønnøysund, enhetsregisteret, organisasjonsnummer, norway company lookup, Companies House, company number, uk company lookup, uk company search, confirmation statement, business registry, company registry, MCP.\n\n`GET /llms-full.txt` is the complete reference for an LLM caller: every endpoint, every error code and what to do about it, and the full `CompanyReport` field list.","contact":{"url":"https://github.com/foretak/registry-mcp"},"license":{"name":"MIT"},"version":"0.2.0"},"paths":{"/health":{"get":{"tags":["meta"],"summary":"Liveness probe","description":"Liveness check for monitoring — returns 200 with the running version and the currently supported country codes whenever the process is up. Not part of the data API: it does no upstream call and has no error mode. Do not call it in a polling loop faster than your monitoring actually needs.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"example":{"status":"ok","version":"0.2.0","countries":["NO"]}}}}}}},"/v1/countries":{"get":{"tags":["meta"],"summary":"List supported countries and registries","description":"Discover which national business registries this service can answer for right now, and what each one's identifier looks like — for Norway, an organisasjonsnummer (orgnr) looked up in Enhetsregisteret (Brønnøysundregistrene, brreg). Call this before your first lookup in a country you have not used here before, or whenever a user names a country you are unsure is supported. Never hard-code this list in your own code — it grows as country modules are added, with no change to any other endpoint's shape. A 500 here means a bug on our side; retry once, this call has no other error mode.","operationId":"get_countries_v1_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CountriesResponse"},"example":{"countries":[{"country":"GB","registry":"companies-house","name":"Companies House (United Kingdom)","id_scheme":"company number","id_example":"00445790","id_description":"A UK company registration number (CRN): 8 characters, either 8 digits or a two-letter prefix and 6 digits. Shorter numbers are zero-padded, so 445790 is written 00445790. There is no check digit.","source_url":"https://api.company-information.service.gov.uk","license":"Crown copyright — Companies House public register, free to re-use","is_stub":false,"requires_api_key":true,"api_key_env":"COMPANIES_HOUSE_API_KEY"},{"country":"NO","registry":"brreg","name":"Enhetsregisteret (Brønnøysundregistrene)","id_scheme":"organisasjonsnummer","id_example":"923609016","id_description":"A Norwegian organisasjonsnummer (orgnr): nine digits, the ninth a MOD11 check digit. Written '923 609 016' or '923609016'; a VAT number adds 'MVA'.","source_url":"https://data.brreg.no/enhetsregisteret/api","license":"NLOD 2.0","is_stub":false,"requires_api_key":false}]}}}}}}},"/v1/{country}/company/{id}":{"get":{"tags":["companies"],"summary":"Full report for one registered entity","description":"Fetch everything this service knows about one company by its national identifier — for Norway (`NO`), an organisasjonsnummer/orgnr (nine digits, MOD11 check digit, e.g. `923609016`) held in Enhetsregisteret (Brønnøysundregistrene, brreg). Use this once you have the identifier — from the user, an invoice, a contract, or a `search` hit's `id`; the identifier is normalised for you, so spaces, dots and a VAT suffix ('NO...MVA') are all accepted. Read `notes` before acting on the result: it carries caveats such as bankruptcy or an unclassified legal form. On `invalid_id` (400), fix the identifier or call `search` with the company name instead of retrying the same string; on `not_found` (404), the identifier is well-formed but no such entity exists — call `search`. On `upstream_error`/`upstream_timeout` (502/504) the national register is unavailable; it has already been retried once here, so wait roughly a minute and retry at most once more yourself.","operationId":"get_company_v1__country__company__id__get","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string","title":"Country"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyReport"},"example":{"country":"NO","registry":"brreg","id":"923609016","id_formatted":"923 609 016","id_scheme":"organisasjonsnummer","name":"EQUINOR ASA","previous_names":["STATOIL ASA"],"legal_form_code":"ASA","legal_form":"Public limited company","legal_form_local":"Allmennaksjeselskap","limited_liability":true,"has_board_duty":true,"has_annual_accounts_duty":true,"status":"active","status_detail":"Registered and active in Enhetsregisteret.","is_active":true,"registered_at":"1995-03-12","vat_registered":true,"vat_number":"NO923609016MVA","in_business_register":true,"employees":21239,"employees_reported":true,"industry_codes":[{"code":"06.100","description":"Utvinning av råolje","scheme":"NACE","rank":1}],"share_capital":5976872600.0,"share_capital_currency":"NOK","business_address":{"lines":["Forusbeen 50"],"postal_code":"4035","city":"STAVANGER","municipality":"STAVANGER","municipality_code":"1103","country_code":"NO","country_name":"Norge"},"confidence":1.0,"confidence_basis":"exact identifier lookup in Enhetsregisteret","cached":false,"fetched_at":"2026-09-03T09:12:44Z","source":"Enhetsregisteret (Brønnøysundregistrene)","source_url":"https://data.brreg.no/enhetsregisteret/api/enheter/923609016","license":"NLOD 2.0","notes":[]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/{country}/search":{"get":{"tags":["companies"],"summary":"Find entities by name","description":"Turn a company name into candidate identifiers — for Norway (`NO`), a name search against Enhetsregisteret (Brønnøysundregistrene, brreg). Use this when the user gave you a name rather than a number, then call the company endpoint with the `id` of the right hit for the full report; a search hit is deliberately thin and must not be acted on directly. `limit` is 1-100 (default 10); a value outside that range is a `bad_request` (400), not a silent clamp, so you learn the real range. Zero hits is not an error — `hits` is `[]`, `total` is `0`, and `hint` says what to try next: Norwegian names are registered upper-case and often carry an 'AS', 'ASA' or 'NUF' suffix worth dropping before concluding a company does not exist.","operationId":"search_companies_v1__country__search_get","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string","title":"Country"}},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Company name to search for.","title":"Q"},"description":"Company name to search for."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","description":"Maximum hits to return, 1-100.","default":10,"title":"Limit"},"description":"Maximum hits to return, 1-100."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResult"},"example":{"country":"NO","registry":"brreg","query":"equinor","hits":[{"country":"NO","registry":"brreg","id":"923609016","name":"EQUINOR ASA","legal_form_code":"ASA","legal_form":"Public limited company","status":"active","city":"STAVANGER","municipality":"STAVANGER","registered_at":"1995-03-12","is_subunit":false,"confidence":0.95,"confidence_basis":"name matches the query exactly (case-insensitive)","source_url":"https://data.brreg.no/enhetsregisteret/api/enheter/923609016"}],"total":240,"truncated":true,"cached":false,"fetched_at":"2026-09-03T09:13:02Z","hint":"240 companies match. Call lookup_company with the id of the right hit for the full report."}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/{country}/company/{id}/deadlines":{"get":{"tags":["companies"],"summary":"Next filing deadline of each kind","description":"Compute the next occurrence of every statutory filing obligation this entity faces, as of `today` (`YYYY-MM-DD`; defaults to the server's current UTC date — pass it explicitly whenever you want a reproducible answer). Deadlines are computed, never fetched, so the same entity and `today` always produce the same list. Quote `due_date`, not `statutory_date` — it already accounts for weekends and public holidays; `applies_because` states the legal form or flag (and any assumption) behind each date, quote it rather than presenting a date as unconditional fact. An empty list is a real answer for a bankrupt, deleted or compulsorily-liquidated entity, or a branch/sub-unit — `notes` explains why. On `bad_request` (400), `today` was not `YYYY-MM-DD` — fix the format and retry.","operationId":"get_deadlines_v1__country__company__id__deadlines_get","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string","title":"Country"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"today","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Date to compute from, YYYY-MM-DD. Defaults to the server's current UTC date.","title":"Today"},"description":"Date to compute from, YYYY-MM-DD. Defaults to the server's current UTC date."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeadlineReport"},"example":{"country":"NO","registry":"brreg","company_id":"923609016","company_name":"EQUINOR ASA","today":"2026-01-15","deadlines":[{"country":"NO","registry":"brreg","kind":"shareholder_register_statement","name":"Shareholder register statement","local_name":"Aksjonærregisteroppgaven (RF-1086)","authority":"Skatteetaten","statutory_date":"2026-01-31","due_date":"2026-02-02","rolled_forward":true,"period_label":"2025","recurrence":"annual","mandatory":true,"applies_because":"AS and ASA companies must file the shareholder register statement (RF-1086) with Skatteetaten. Assumes a calendar-year accounting period.","days_until":18}],"notes":["One or more of these deadlines assume a calendar-year accounting period; a deviating accounting year would shift the real dates."]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/{country}/validate/{id}":{"get":{"tags":["companies"],"summary":"Check an identifier's format and checksum","description":"Validate a national identifier's format and checksum with no network round-trip to the national register — for Norway (`NO`), the organisasjonsnummer/orgnr MOD11 check digit. Use this to validate user input or a spreadsheet column before spending lookups: it is cheap and instant, so prefer it to a speculative company lookup. An invalid identifier is a normal `200` with `valid: false` and a `reason` — this endpoint answers a question, it does not fail. A well-formed identifier does not mean the entity exists; follow a valid result with the company endpoint if you need facts.","operationId":"validate_id_v1__country__validate__id__get","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string","title":"Country"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResult"},"example":{"country":"NO","registry":"brreg","id_scheme":"organisasjonsnummer","input":"923 609 016","valid":true,"normalized":"923609016","formatted":"923 609 016","reason":"Well-formed organisasjonsnummer for NO. A valid identifier does not mean the entity exists — call lookup_company (MCP) or GET /v1/{country}/company/{id} (REST) to find out."}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Address":{"properties":{"lines":{"items":{"type":"string"},"type":"array","title":"Lines","description":"Street or PO-box lines exactly as the registry supplies them."},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code","description":"Postal / ZIP code."},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"Post town."},"municipality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Municipality","description":"Municipality name."},"municipality_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Municipality Code","description":"National municipality code, if the registry has one."},"country_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Code","description":"ISO-3166-1 alpha-2 country code of the address itself."},"country_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Name","description":"Country name as registered."}},"additionalProperties":false,"type":"object","title":"Address","description":"A postal or visiting address, flattened to something an LLM can read.\n\n``lines`` keeps the registry's own street/box lines in order; the rest are\nparsed components where the registry provides them."},"CompanyReport":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case, e.g. 'NO'."},"registry":{"type":"string","title":"Registry","description":"Registry slug, e.g. 'brreg'."},"id":{"type":"string","title":"Id","description":"Canonical national identifier, digits/letters only, no spaces or dots."},"id_formatted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id Formatted","description":"The identifier as a local would write it, e.g. '923 609 016'."},"id_scheme":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id Scheme","description":"Name of the identifier scheme, e.g. 'organisasjonsnummer'."},"name":{"type":"string","title":"Name","description":"Current registered name."},"previous_names":{"items":{"type":"string"},"type":"array","title":"Previous Names","description":"Former registered names, newest first."},"legal_form_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form Code","description":"National legal-form code, e.g. 'AS', 'ASA', 'ENK'."},"legal_form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form","description":"English label, e.g. 'Private limited company'."},"legal_form_local":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form Local","description":"Local label, e.g. 'Aksjeselskap'."},"limited_liability":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Limited Liability","description":"True when owners are not personally liable for debts."},"has_board_duty":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Board Duty","description":"True when this legal form must have a registered board."},"has_annual_accounts_duty":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Annual Accounts Duty","description":"True when this legal form must file annual accounts with the state."},"status":{"$ref":"#/components/schemas/CompanyStatus","description":"Normalised lifecycle status.","default":"unknown"},"status_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Detail","description":"One sentence in English explaining the status and the flag it came from."},"is_active":{"type":"boolean","title":"Is Active","description":"Convenience mirror of `status == active`, so agents need no enum table.","default":false},"registered_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Registered At","description":"Date first entered in the central register."},"founded_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Founded At","description":"Incorporation / foundation date."},"business_register_registered_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Business Register Registered At","description":"Date entered in the commercial/business register, where that is separate."},"bankruptcy_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Bankruptcy Date","description":"Date bankruptcy was opened."},"deregistered_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Deregistered At","description":"Date the entity was deleted from the register."},"vat_registered":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Vat Registered","description":"Registered for VAT (Norway: Merverdiavgiftsregisteret)."},"vat_registered_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Vat Registered At","description":"Date of VAT registration."},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number","description":"VAT identifier if it differs from `id` (Norway: id + 'MVA')."},"in_business_register":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"In Business Register","description":"Listed in the commercial register (Norway: Foretaksregisteret)."},"registers":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Registers","description":"Other national sub-registers this entity is or is not in, keyed by a lower-case slug, e.g. {'stiftelsesregisteret': false}."},"employees":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Employees","description":"Registered number of employees. None = not reported."},"employees_reported":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Employees Reported","description":"Whether the registry holds an employee figure at all (distinguishes 0 from unknown)."},"industry_codes":{"items":{"$ref":"#/components/schemas/IndustryCode"},"type":"array","title":"Industry Codes","description":"Industry classifications, primary first."},"sector_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector Code","description":"Institutional sector code."},"sector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector","description":"Institutional sector description."},"purpose":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Purpose","description":"Statutory purpose / objects clause, joined into one string."},"activity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity","description":"Free-text description of actual activity."},"share_capital":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Share Capital","description":"Registered share capital."},"share_capital_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Share Capital Currency","description":"ISO-4217 code for `share_capital`."},"business_address":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}],"description":"Visiting/registered office."},"postal_address":{"anyOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}],"description":"Postal address."},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website","description":"Website as registered."},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Contact email as registered."},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Contact phone as registered."},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"Identifier of the parent/owning entity, if any."},"is_subunit":{"type":"boolean","title":"Is Subunit","description":"True when this record is a branch/sub-unit, not a legal entity.","default":false},"in_group":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"In Group","description":"Part of a corporate group."},"last_annual_accounts_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Annual Accounts Year","description":"Most recent financial year for which accounts were filed."},"published_deadlines":{"items":{"$ref":"#/components/schemas/PublishedDeadline"},"type":"array","title":"Published Deadlines","description":"Filing dates the upstream register publishes for this entity itself, carried verbatim. Empty for a register that publishes none — most of them. This is the input `Registry.deadlines(report, today)` needs to prefer the register's own figure over any calculation (DECISIONS.md D-018), and it is what keeps that method the pure function of (report, today) its contract promises."},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"How sure we are this record is the entity the caller meant (D-005).","default":1.0},"confidence_basis":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confidence Basis","description":"Why that confidence, e.g. 'exact identifier lookup'."},"cached":{"type":"boolean","title":"Cached","description":"True when served from our cache rather than a live fetch.","default":false},"fetched_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fetched At","description":"UTC timestamp of the live fetch this record came from."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Human-readable source name, e.g. 'Enhetsregisteret (brreg.no)'."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Direct URL of the upstream record, for citation."},"license":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License","description":"Licence of the upstream data, e.g. 'NLOD 2.0'."},"notes":{"items":{"type":"string"},"type":"array","title":"Notes","description":"Caveats an agent should surface to the user, plain English, one per item."}},"additionalProperties":false,"type":"object","required":["country","registry","id","name"],"title":"CompanyReport","description":"Everything `registry-mcp` knows about one registered entity.\n\nThis is the single most important shape in the project. It is returned\nverbatim by ``GET /v1/{country}/company/{id}`` and by the MCP tool\n``lookup_company``.\n\nA registry module fills what its national register publishes and leaves the\nrest ``None``. Nothing here is Norway-specific; ``registries/no/`` maps\nEnhetsregisteret's fields onto it (see ``NORBIZ_SPEC.md`` §3)."},"CompanyStatus":{"type":"string","enum":["active","under_liquidation","under_compulsory_liquidation","bankrupt","dissolved","deleted","unknown"],"title":"CompanyStatus","description":"Normalised lifecycle status of a registered entity.\n\nThe mapping from national flags to these values is the registry module's\njob; the values themselves are country-neutral so an agent can branch on\nthem without knowing which country it is looking at.\n\nPrecedence when several national flags are true is defined per registry\n(for Norway: ``deleted`` > ``bankrupt`` > ``under_compulsory_liquidation``\n> ``under_liquidation`` > ``active``)."},"CountriesResponse":{"properties":{"countries":{"items":{"$ref":"#/components/schemas/CountryInfo"},"type":"array","title":"Countries","description":"One row per registry that can answer right now, sorted by country code."}},"additionalProperties":false,"type":"object","title":"CountriesResponse","description":"The answer to \"which countries can you answer for?\".\n\nThe only shape the discovery operation returns, on both surfaces\n(``DECISIONS.md`` D-012): REST ``GET /v1/countries`` and the MCP tool\n``list_countries``. Before D-012 each surface re-derived this envelope from\n``Registry.describe()`` on its own, which is how the two could have drifted\n— REST validated the dict through a private model that silently *dropped* an\nunrecognised key while MCP passed the raw dict through and *kept* it."},"CountryInfo":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case, e.g. 'NO'."},"registry":{"type":"string","title":"Registry","description":"Registry slug, e.g. 'brreg'."},"name":{"type":"string","title":"Name","description":"Human-readable register name."},"id_scheme":{"type":"string","title":"Id Scheme","description":"What the national identifier is called locally, e.g. 'organisasjonsnummer'."},"id_example":{"type":"string","title":"Id Example","description":"A real, valid identifier the caller can use to smoke-test the service."},"id_description":{"type":"string","title":"Id Description","description":"One sentence describing the identifier's format."},"source_url":{"type":"string","title":"Source Url","description":"Base URL of the upstream registry API, for citation."},"license":{"type":"string","title":"License","description":"Licence of the upstream data, e.g. 'NLOD 2.0'."},"is_stub":{"type":"boolean","title":"Is Stub","description":"True for example/template modules, which are hidden from the public list unless stubs are explicitly requested.","default":false},"requires_api_key":{"type":"boolean","title":"Requires Api Key","description":"True when this registry's upstream API needs a credential the operator must supply. A self-hosted deployment that has not set it gets upstream_error on every call to this country (DECISIONS.md D-017).","default":false},"api_key_env":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Env","description":"Name of the environment variable holding that credential, e.g. 'COMPANIES_HOUSE_API_KEY'. None when no key is needed. Never the key itself."}},"additionalProperties":false,"type":"object","required":["country","registry","name","id_scheme","id_example","id_description","source_url","license"],"title":"CountryInfo","description":"One supported country/registry pair, as returned by the discovery operation.\n\nBuilt by ``Registry.country_info()`` from the class attributes of a\n:class:`~registry_mcp.core.registry.Registry` subclass — the same nine\nvalues ``Registry.describe()`` has always emitted, now with a type\n(``DECISIONS.md`` D-012).\n\nThis is the country-neutral half of the contract even though its *values*\nname a country: it carries no report data, so unlike every other returned\nmodel it is a row *about* a registry rather than a document *from* one."},"Deadline":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case."},"registry":{"type":"string","title":"Registry","description":"Registry slug that produced this deadline."},"kind":{"type":"string","title":"Kind","description":"Stable machine slug for the obligation, e.g. 'annual_accounts', 'tax_return', 'vat_return', 'shareholder_register_statement'. Unique within a country."},"name":{"type":"string","title":"Name","description":"Short English label, e.g. 'Annual accounts filing'."},"local_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Local Name","description":"The name a local accountant would use, e.g. 'Årsregnskap'."},"authority":{"type":"string","title":"Authority","description":"Who receives the filing, e.g. 'Regnskapsregisteret', 'Skatteetaten'."},"statutory_date":{"type":"string","format":"date","title":"Statutory Date","description":"The date named by law, before weekend/holiday roll-forward."},"due_date":{"type":"string","format":"date","title":"Due Date","description":"The date the caller must actually file by (statutory date rolled forward)."},"rolled_forward":{"type":"boolean","title":"Rolled Forward","description":"True when due_date differs from statutory_date because of a non-working day.","default":false},"period_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Period Label","description":"Which period this filing covers, e.g. '2025' or '2026 term 3 (May–Jun)'."},"period_start":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Period Start","description":"First day of the covered period."},"period_end":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Period End","description":"Last day of the covered period."},"recurrence":{"$ref":"#/components/schemas/DeadlineRecurrence","description":"How often the obligation repeats.","default":"annual"},"mandatory":{"type":"boolean","title":"Mandatory","description":"True when the obligation follows from the legal form alone. False when it depends on facts we cannot see (e.g. VAT turnover threshold) — in that case applies_because explains the assumption.","default":true},"applies_because":{"type":"string","title":"Applies Because","description":"One sentence an agent can quote to the user explaining why this deadline applies to this company, including any assumption made."},"days_until":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days Until","description":"due_date minus the `today` the calculation was run with. Negative = overdue."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Authoritative page describing the obligation."}},"additionalProperties":false,"type":"object","required":["country","registry","kind","name","authority","statutory_date","due_date","applies_because"],"title":"Deadline","description":"One filing obligation with a concrete calendar date.\n\nDeadlines are *computed*, never fetched: a registry module derives them\nfrom the entity's legal form and status plus a ``today`` parameter, so the\nsame input always produces the same output and tests are deterministic.\n\n``due_date`` is always the date the caller should act on; ``statutory_date``\nis the date the statute names before any weekend/holiday roll-forward."},"DeadlineRecurrence":{"type":"string","enum":["annual","bimonthly","quarterly","monthly","one_off"],"title":"DeadlineRecurrence","description":"How often a deadline repeats."},"DeadlineReport":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case."},"registry":{"type":"string","title":"Registry","description":"Registry slug, e.g. 'brreg'."},"company_id":{"type":"string","title":"Company Id","description":"Canonical national identifier the deadlines were computed for."},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name","description":"Registered name, so the caller can echo it back to a user."},"today":{"type":"string","format":"date","title":"Today","description":"The date 'next occurrence' was computed from, inclusive. Echoed back so the answer is reproducible and an agent can tell a cached answer from a fresh one."},"deadlines":{"items":{"$ref":"#/components/schemas/Deadline"},"type":"array","title":"Deadlines","description":"One entry per obligation kind, always the next occurrence, sorted by due_date. An empty list is a real answer, not an error — read `notes` for why."},"notes":{"items":{"type":"string"},"type":"array","title":"Notes","description":"Caveats to surface to the user, carried over from the company report: why the list is empty, an unclassified legal form, a status that suspends filing."}},"additionalProperties":false,"type":"object","required":["country","registry","company_id","today"],"title":"DeadlineReport","description":"The answer to \"what must this company file, and by when?\".\n\nThis is the **only** shape the deadlines operation returns, on both\nsurfaces (``DECISIONS.md`` D-010): REST\n``GET /v1/{country}/company/{id}/deadlines`` and the MCP tool\n``company_deadlines`` each emit ``model_dump(mode=\"json\")`` of this model,\nunchanged. Neither surface may return a bare ``list[Deadline]``, because a\nlist has nowhere to put ``today`` or ``notes`` — and an empty list without\na note is indistinguishable from a bug.\n\nBuild it with ``Registry.deadline_report(report, today)``; do not construct\nit in a surface."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status","default":"ok"},"version":{"type":"string","title":"Version"},"countries":{"items":{"type":"string"},"type":"array","title":"Countries"}},"type":"object","required":["version","countries"],"title":"HealthResponse"},"IndustryCode":{"properties":{"code":{"type":"string","title":"Code","description":"The code as published, e.g. '06.100'."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Registry's own description."},"scheme":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheme","description":"Classification scheme, e.g. 'NACE' or the national variant name."},"rank":{"type":"integer","minimum":1.0,"title":"Rank","description":"1 = primary activity, 2 = second, and so on.","default":1}},"additionalProperties":false,"type":"object","required":["code"],"title":"IndustryCode","description":"An industry classification code (NACE / SIC / national equivalent)."},"PublishedDeadline":{"properties":{"kind":{"type":"string","title":"Kind","description":"The same machine slug the country module uses for the matching `Deadline.kind`, e.g. 'annual_accounts'. Unique within a report."},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date","description":"The date the register itself publishes for this filing. `None` when the register names a period but no date — the country module may still be able to compute one from `period_end`."},"period_start":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Period Start","description":"First day of the period this filing covers, if published."},"period_end":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Period End","description":"Last day of the period this filing covers, if published. This, not an accounting reference date, is what a statutory period runs from."},"overdue":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Overdue","description":"The register's own overdue flag, if it publishes one. Corroboration only: it is computed against the register's today, not the caller's, so `Deadline.days_until < 0` is the authoritative answer."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Where the date came from upstream, e.g. 'accounts.next_accounts.due_on'. Opaque to core; the country module turns it into `applies_because` prose."}},"additionalProperties":false,"type":"object","required":["kind"],"title":"PublishedDeadline","description":"A filing obligation exactly as the *register itself* publishes it.\n\nThe counterpart to :class:`Deadline`, and deliberately much smaller.\n:class:`Deadline` is *ours*: computed, English-labelled, dated against a\ncaller-supplied ``today``. This is *theirs*: whatever the upstream register\nstates about the obligation, carried verbatim, with no interpretation and\nno arithmetic.\n\nIt exists because some registers do the filing arithmetic themselves and\npublish the answer — Companies House publishes\n``accounts.next_accounts.due_on`` and ``confirmation_statement.next_due``,\nwhich already account for accounting-reference-date changes, shortened and\nextended periods, and administrative extensions that no outside calculation\ncan see (``DECISIONS.md`` D-016(a), D-018). A registry whose upstream\npublishes such a date fills this list at lookup time, and its\n:meth:`Registry.deadlines` then merges: the published date wins, a\ncomputation fills the gaps. A registry whose upstream publishes nothing —\nBrønnøysundregistrene, and every register that only states the statute —\nleaves the list empty and loses nothing.\n\nNothing in ``core/`` interprets any field here. ``kind`` and ``source``\nare opaque strings owned by the country module; ``core`` only carries them\nacross the lookup → deadlines boundary so that\n``Registry.deadlines(report, today)`` can stay the pure function of\n``(report, today)`` that its contract promises."},"SearchHit":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case."},"registry":{"type":"string","title":"Registry","description":"Registry slug."},"id":{"type":"string","title":"Id","description":"Canonical national identifier — feed this to lookup."},"name":{"type":"string","title":"Name","description":"Registered name."},"legal_form_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form Code","description":"National legal-form code."},"legal_form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form","description":"English legal-form label."},"status":{"$ref":"#/components/schemas/CompanyStatus","description":"Normalised lifecycle status.","default":"unknown"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"Post town of the business address."},"municipality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Municipality","description":"Municipality of the business address."},"registered_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Registered At","description":"Date entered in the register."},"is_subunit":{"type":"boolean","title":"Is Subunit","description":"True for branches / sub-units.","default":false},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Match confidence for this hit (D-005).","default":0.5},"confidence_basis":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confidence Basis","description":"Why that confidence."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Upstream record URL."}},"additionalProperties":false,"type":"object","required":["country","registry","id","name"],"title":"SearchHit","description":"One candidate from a name search.\n\nDeliberately thin: enough for an agent to pick the right entity and then\ncall ``lookup_company`` with ``id`` for the full report."},"SearchResult":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case."},"registry":{"type":"string","title":"Registry","description":"Registry slug."},"query":{"type":"string","title":"Query","description":"The name that was searched for."},"hits":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Hits","description":"Best matches, best first: always sorted by `confidence` descending. Hits that score equally keep the order the upstream register returned them in."},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total matches upstream, which may exceed len(hits).","default":0},"truncated":{"type":"boolean","title":"Truncated","description":"True when `total` exceeds the returned hits.","default":false},"cached":{"type":"boolean","title":"Cached","description":"Served from cache.","default":false},"fetched_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fetched At","description":"UTC timestamp of the fetch."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"What to do next, e.g. 'call lookup_company with the id of the right hit'."}},"additionalProperties":false,"type":"object","required":["country","registry","query"],"title":"SearchResult","description":"Envelope returned by ``search`` — hits plus what the agent needs next."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ValidationResult":{"properties":{"country":{"type":"string","title":"Country","description":"ISO-3166-1 alpha-2, upper-case."},"registry":{"type":"string","title":"Registry","description":"Registry slug, e.g. 'brreg'."},"id_scheme":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id Scheme","description":"Name of the identifier scheme, e.g. 'organisasjonsnummer'."},"input":{"type":"string","title":"Input","description":"The identifier exactly as the caller supplied it."},"valid":{"type":"boolean","title":"Valid","description":"True when the identifier passes this country's format and checksum."},"normalized":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Normalized","description":"Canonical form to pass to lookup, e.g. '923609016'. None when invalid."},"formatted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Formatted","description":"The identifier as a local would write it, e.g. '923 609 016'. None when invalid."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"One English sentence saying why it is valid, or what failed."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"What to do next when `valid` is false — the same hint the invalid_id error carries. None when valid: the next call is simply lookup."}},"additionalProperties":false,"type":"object","required":["country","registry","input","valid"],"title":"ValidationResult","description":"The answer to \"is this identifier well-formed?\" — no network call.\n\nThe only shape the validation operation returns, on both surfaces\n(``DECISIONS.md`` D-010): REST ``GET /v1/{country}/validate/{id}`` and the\nMCP tool ``validate_company_id``.\n\nNote that an invalid identifier is **not** an error here: this operation\nanswers a question, so it returns ``valid=False`` with a ``reason`` and a\n``hint`` rather than raising. That is the one deliberate exception to\n``DECISIONS.md`` D-007's \"every expected failure is a raised\n``RegistryError``\" — and the reason ``hint`` is carried on this model.\n\nBuild it with ``Registry.validate(id)``; do not construct it in a surface."}}},"tags":[{"name":"companies","description":"Look up, search and compute filing deadlines for one registered entity."},{"name":"meta","description":"Discover what this service supports and whether it is up."}]}