tidybrreg 0.6.0
The split
tidybrreg is now the narrow, typed client to the register: exactly sixteen exports, each wrapping one endpoint of Enhetsregisteret, the Fullmakt service, Regnskapsregisteret, or SSB KLASS.
brreg_entity() brreg_search() brreg_underenheter() brreg_children()
brreg_roles() brreg_roles_legal() brreg_konsern()
brreg_signatur() brreg_prokura()
brreg_regnskap()
brreg_updates() brreg_update_fields()
brreg_download()
brreg_label() get_brreg_dic() brreg_validate()The managed-state subsystem (snapshot store, brreg_sync(), replay, changelog queries, flows, panels, series, tsibble conversion, events, annotations, name history, harmonisation, director networks, survival data, brreg_board_summary(), diff_roller_state(), brreg_status()) has moved, unchanged in behaviour, to the companion package tidybrregpanel in the tidybrregpanel/ directory of this repository. It depends on tidybrreg and is built alongside it on r-universe. The pre-split implementation remains at commit c9bd992.
The Model Context Protocol server tidybrregmcp (tidybrregmcp/) exposes the sixteen exports as MCP tools, deriving every tool’s documentation and schema from this package’s help pages, and writes a manifest (mcp_manifest()) from which tidybrreg-mcp (tidybrreg-mcp-node/, on npm) serves the same catalogue without R. The package ships inst/extdata/endpoints.json, a machine-readable contract of every export’s endpoint, query mapping and status handling, held to the functions by tests/testthat/test-endpoints.R.
Tidy shape
- Every tibble the package returns is now tidy in Wickham’s sense, with no cell holding more than one value. Arrays of objects the register nests inside an entity (
historiskeNavn,paategninger) were serialized to JSON text in one cell;brreg_entity(),brreg_search()and the JSON bulk ofbrreg_download()now return them as list-columns of tibbles (historiske_navn,annotations), one row per element with snake_case columns,NULLfor an entity without them, sotidyr::unnest()gives the table of the other unit. An empty array emits no column. Arrays of strings (addresses,purpose,activity) are line fragments of one value and keep the"\n"join.brreg_regnskap()’sinconsistenciesis a list-column of the failing identity names (zero-length when none) instead of a newline-joined string. The Node MCP server returns the same nesting as JSON arrays. tidybrregpanel’s bootstrap reads both the nested column and the JSON text of older snapshots.
New: annual accounts
- New
brreg_regnskap()retrieves an entity’s annual accounts from the Regnskapsregisteret open API (GET /regnskapsregisteret/regnskap/{orgnr}, optionalyearandaccount_type). The result is nested: one row per filing with its identity, period, currency, legal form and audit flags as scalar columns, and the figures as anitemstibble per filing that keeps the API’s structure (statement,group,item,path,level,is_sum,value), so totals and components are never added together by accident;tidyr::unnest()gives the long form. Every filing is flagged for the accounting identities it fails (consistent,inconsistencies; balance sheet balances, totals equal their components, results follow from their parts, within 1 unit of currency), since the register publishes filings as filed. The field, group and identity maps are published underbrreg_regnskapinendpoints.jsonfor the MCP servers, and the flattening is verified against every structural shape of the 1,129,601 filings in the brreg-json-fetcherdata-2026-10release (data-raw/regnskap/). The maps are bundled datasets built indata-raw/build_dictionaries.Rthe way the entity and role dictionaries are:regnskap_dict(asfield_dict, applied by the same rename and coercion rule),regnskap_groupsandregnskap_items(asrole_groupsandrole_types, withname_enandname_no).brreg_regnskap(type = "label")andbrreg_label()translateitem,group,statementandlegal_formin English or Norwegian, inside the nesteditemsas well as aftertidyr::unnest();brreg_label()accepts the dictionaries"regnskap_item"and"regnskap_group"for vectors. Entities whose accounting plan the API does not serve (banks, insurers, pension funds) return the typed empty withunsupported_planand a newtidybrreg_warning_unsupported_planinstead of an error.
Breaking changes
brreg_download()reads the JSON bulk only: theformatargument is gone and/enheter/lastned/csvand/underenheter/lastned/csvare no longer requested. The JSON bulk is the representation the single-entity lookups and the update feeds use, so a bulk row, a lookup and a patch now agree on every field, nested arrays included; the CSV was BRREG’s own flattening with its own separators and a presence flag in place of the annotation array. The cache file is{type}_bulk.json.gz.field_dictloses the two CSV-only spellings (registreringsdatoIEnhetsregisteret,registreringsdatoAntallAnsatteNavAaregisteret). The array is read in batches of complete objects (read_json_objects()), each parsed on its own, so the whole decoded tree of a million entities is never in memory at once, and a sample is read without touching the rest of the file (parse_bulk_json(n_max = )). The batches are bound one column at a time (bind_batches()) rather than withdplyr::bind_rows(), which held every batch and the result together. On the live register (2026-11-02) the fullenheterparse takes 14.6 minutes and peaks at 4.0 GB for a 1.6 GB tibble of 1,179,853 rows;underenheter7.1 minutes and 2.0 GB for 870,571 rows. The roller bulk (/roller/totalbestand) is streamed the same way:parse_roles_bulk()decodes and flattens one batch of entities at a time instead of holding the decoded tree of the whole register, which exceeded 7 GB: 3,426,438 roles in 11.1 minutes at a 2.3 GB peak.brreg_download()documents the memory and time of the full parse. tidybrregpanel’s parquet layer carries the nested columns on both backends: arrow’slist_ofvectors are read back as plain lists so state binds with freshbrreg_entity()rows (the replay sync failed on the type clash), and nanoparquet, which writes no nested types, stores them as JSON text named in the file metadata and restores the tibbles on read. Before arrow writes a list-column its tibbles are widened to the union of their fields in one order, since arrow derives the struct from the first element and a row added by a sync carries the response’s key order. tidybrregpanel follows:brreg_snapshot()andbrreg_sync()loseformat,brreg_import()takes a JSON bulk file, and the CSV flag-then-fetch bootstrap of annotations is gone.brreg_download()supportstype_output = "tibble"and"path"only. The Arrow backend is withdrawn: BRREG’s CSV fails inarrow::read_csv_arrow()on embedded newlines in quoted addresses, its JSON bulk is a pretty-printed array rather than NDJSON, and the path bypassed the dictionary. arrow, nanoparquet and duckdb are no longer Suggests.get_brreg_dic()returns columnscode,name,level(namereplacesname_en, which was wrong forlang = "no"), with attributessource,langandvintage, and gainssource = c("auto", "klass", "bundled")andrefresh.brreg_update_fields()and thechangescolumn ofbrreg_updates()gain apathcolumn holding the raw JSON pointer of each patch leaf.Timestamps from the update feeds are
POSIXctin UTC (previously parsed as local time);sincekeeps the precision of aPOSIXctand is sent as a UTC instant; aDatemeans midnight UTC.brreg_entity()always carriesdeleted(logical) andregistry.Valid empty results are typed zero-row tibbles everywhere (previously zero-column
tibble()): search results carryorg_nr,name,legal_form; roles, legal roles, konsern, signatur and prokura carry their full documented column set; update feeds carry their four (or seven) columns.The per-entity endpoints (
brreg_roles(),brreg_roles_legal(),brreg_konsern(),brreg_signatur(),brreg_prokura()) no longer warn on HTTP 404; they return the typed empty with attributehttp_status = 404L. HTTP 410 warns about deletion; other HTTP errors abort.Loading or attaching the package performs no filesystem or network access: the startup message about missing bulk data and the undocumented
.onLoadoverride of bundled dictionaries from RDS files are gone.
Typed conditions
Every error and warning now carries a class and fields; see ?tidybrreg_conditions. Errors: tidybrreg_error_invalid_org_nr, _invalid_filter, _not_found, _http, _result_window, _invalid_since, _no_bundled_dictionary, _rate_limited, _connection. Warnings: tidybrreg_warning_deleted, _partial, _truncated, _unknown_member, _bundled_fallback, _parse, _unresolved, _unsupported_plan.
Request layer
The retry rule follows what the brreg-json-fetcher collector observed live. 500, 502 and 504 are transient like 429 and 503 (three tries, exponential pause), except the Regnskapsregisteret 500 that names an unsupported accounting plan, which is a property of the entity and is final. The register’s rate limit sometimes arrives as HTTP 200 with a text/plain body Too many requests; it was an unreadable-body crash and is now retried and, when it persists, a typed tidybrreg_error_rate_limited. A connection failure (DNS, TCP, TLS, dropped connection) is retried the same number of times and then a tidybrreg_error_connection; the bulk download goes through the same rule, so a connection reset mid-download is retried and a server error aborts with the typed tidybrreg_error_http. The live audit probes the fetcher’s two deleted controls (931264338, 991128972) as deleted here too. The Node server applies the same rule from endpoints.json.
Fixes to the retained core
brreg_entity()models deletion consistently: HTTP 200 withrespons_klasse = "SlettetEnhet"(the form BRREG uses for nearly all deletions) and HTTP 410 both yielddeleted = TRUE, a parseddeletion_date, every other observed field, and a warning. A vector of several organization numbers is rejected instead of silently querying the first. Nested HAL_linksblocks (organisasjonsform._links.self.href) are stripped at any depth on both the live and bulk paths.brreg_search()aborts on unsupported filters (bankruptfor underenheter), aborts withtidybrreg_error_result_windowbefore returning a partial population when a query exceeds BRREG’s 10,000 result window, reports truncation withcomplete = FALSEand a warning, aborts on a first-page HTTP failure, and warns on a later one instead of returning a silently partial result.brreg_updates()andbrreg_update_fields()share one paginator: first-page failures abort, later failures warn and return the pages read withcomplete = FALSE; stopping onmax_pageswith a full last page warns; results carrycomplete,pages_fetchedandcursor. Members of an update object the package does not model pass through as columns with atidybrreg_warning_unknown_member. The roller feed honoursmax_pagesandverbose(paginating onafterId) and rejectsinclude_changes. Patch flattening handles any nesting depth identically on the flat and list-column paths.brreg_download()validatesrefresh(TRUE,FALSE,"auto") andcache;cache = FALSEuses a temporary file instead of writing the payload to the cache directory anyway;refresh = "auto"revalidates against bothETagandLast-Modifiedand re-downloads when nothing can prove the cache current; download metadata (last_modifiedas UTCPOSIXct) is attached asattr(x, "brreg_download")and written to a sidecar.Dictionary coercion is one rule on every path: the four numeric
capital_*fields are numeric on bulk CSV as well as live JSON; parse failures are reported for every type, never abort the parse (the diagnostics table used to break on more than twenty failures), and thebrreg_parse_problemstable carriesn_failed. A field the dictionary lists under two spellings keeps both observed values; a snake_case collision between unmapped fields is made unique rather than dropping the second.Dates and instants: konsern relation dates and Fullmakt birth dates accept both ISO and
DD.MM.YYYY;Last-Modifiedheaders parse in any locale.brreg_label()honourslang = "no"for every dictionary, is idempotent, leaves no straynamesattribute on labelled columns, and preserves the input’s attributes (total_matchessurvivescode =).options(tidybrreg.offline = TRUE)keeps labelling off the network; KLASS failures fall back to the bundled dictionaries with a warning stating their vintage.DESCRIPTIONcarries the maintainer’s real address. Provenance, licence and vintage of every bundled table are documented in?tidybrreg_provenance.
tidybrreg 0.5.0
Breaking changes
Parsers no longer fabricate
field_dictcolumns that the payload did not carry.rename_and_coerce()andrename_from_dict()previously materialized every dictionary column as typedNAwhen absent, asserting the full schema regardless of what the register emitted and making a removed upstream field indistinguishable from a present-null one. Emission is now batch-scoped, matching theresignedrule: a column appears when the field is observed anywhere in the batch,NAfor records in that batch lacking it, absent entirely when unobserved.parse_entities()unions across the batch viabind_rows(), so multi-entity results are unchanged except that never-observed columns are gone. Single-entitybrreg_entity()now returns roughly 61-63 columns instead of a fixed 104. This also stops the enheter bulk parse from stamping twelve underenheter-only columns (beliggenhetsadresse.*,oppstartsdato,datoEierskifte,nedleggelsesdato) onto every enhet row.Array-valued fields now join on
"\n"instead of"; "(bulk JSON) or", "(live JSON). Element text routinely contains commas and semicolons but never newlines, so only"\n"can be split back into the observed elements. Arrays of objects are serialized to JSON rather than pasted, which fixeshistoriskeNavnarriving as unusable text frombrreg_entity()on the ~18% of enheter that have name history. The two JSON paths now agree with each other and with BRREG’s own CSV rendering of addresses; note that the CSV joins wrapped-text fields such asvedtektsfestetFormaalwith a space, so those remain non-identical across formats and cannot be reconciled without a per-field rule.brreg_roles(),brreg_roles_legal(), and the roller bulk parser no longer fabricate aresignedcolumn. BRREG removed the underlyingfratraadtfield from the roles REST endpoint,juridiskeroller, and the totalbestand bulk on 2026-06-16, which had leftresignedconstantFALSEfor every role. The column now appears only when a payload actually carriesfratraadt(replays of pre-June data), with within-payload absence keeping the oldFALSEsemantics.diff_roller_state()already intersects its comparison set with the columns present on both sides, so mixed old/new states diff cleanly.brreg_board_summary()gains a column-tolerant active filter; the previous pre-guard collapsed to a scalar whenresignedwas absent and silently disabled the deregistered filter.
New features
New
brreg_konsern()retrieves the hierarchical corporate group structure from the konsernstruktur endpoint BRREG added 2026-06-24. One row per node at any depth (live ASA groups nest six levels), root synthesized at level 0 asKKKK“Øverste mor” to match the endpoint’s CSV variant, children carrying observedknytningsform,grunnlag, and relation dates. The endpoint returns 404 both for nonexistent organization numbers and for live entities in no group;in_corporate_groupfrombrreg_entity()disambiguates.Name history (
historiskeNavn, added by BRREG 2026-06-16/18) is captured.brreg_sync()ingests theadd /historiskeNavn/-CDC patches for both enheter and underenheter into ahistoriske_navnstate table, read by the newbrreg_historical_names(). Rename events land in the changelog asregistry = "historiske_navn",change_type = "name_history_added", carrying the superseded name and its validity interval.brreg_sync()gains aformatargument (default"csv", behaviour-preserving) forwarded to the bootstrap bulk download.historiskeNavnhas no CSV column and no presence flag, so history predating the first sync exists only in the JSON bulk;format = "json"backfills it at bootstrap.
Minor improvements and fixes
extract_paategninger()parses inline annotation arrays on the JSON bulk path. Previously the boolean-flag matcher never fired on the serialized arrays, so a JSON-format bootstrap silently produced an empty påtegninger state; the CSV flag-then-fetch path is unchanged.Documentation corrected against the live bulk files: the enheter and underenheter CSVs are comma-delimited (not semicolon), and
kapital,vedtektsfestetFormaal, andpaategningerall have CSV columns today.historiskeNavnis the JSON-only field, and CSV carries påtegninger only as atrue/falsepresence flag.The API monitor’s schema probe also samples the per-entity roles endpoint and the konsernstruktur endpoint (ASA census guarantees group members in the sample), so roller field removals like
fratraadtand new endpoints are visible to drift detection. # tidybrreg 0.4.0
New features
- New
brreg_signatur()andbrreg_prokura()retrieve signature (signaturrett) and procuration (prokura) authority from the Brønnøysund Fullmakt service (data.brreg.no/fullmakt). Signing authority — who may bind the entity, and under which combination rule — is held in a separate service from the roles inbrreg_roles(), which report only who holds a seat. Each returns one row per person per registered combination, withrule_status/rule_text, birth date, and an Englishrolelooked up fromrole_types(which gains the six signing-mode codesSIGN,SIFE,SIHV,PROK,POFE,POHV).
Minor improvements and fixes
- Request construction is consolidated behind a single internal factory (
brreg_http_req()). The SSB KLASS request infetch_klass()now flows through it — gaining the shared user agent, transient retry, and adata.ssb.no-scoped throttle — instead of being hand-built, andbrreg_req()gains aserviceargument so the Enhetsregisteret and Fullmakt path roots share one configured request. No change to the Enhetsregisteret-facing API.
tidybrreg 0.3.8
Bug fixes
-
brreg_sync()now populatespaategningerstate. The enheter bulk download (CSV) only carries påtegninger as a boolean presence flag, not the annotation content, soextract_paategninger()previously always produced empty state andbrreg_annotations()returned nothing after a sync. The bootstrap now reads the flag (columnannotations) and fetches the actual annotation content per flagged entity from the enheter endpoint. New internal helperfetch_entity_paategninger().
tidybrreg 0.3.7
New features
- New bundled dataset [annotation_infotypes]: maps brreg påtegning
infotypecodes to English descriptions. Sourced from the brreg API reference (NAVN,FADR) and codes observed in live data (role codes used for missing-role annotations); unknown codes pass through. -
brreg_annotations()gains atranslateargument. Withtranslate = TRUEaninfotype_desccolumn with English descriptions (fromannotation_infotypes) is added afterinfotype. DefaultFALSE, so existing behaviour is unchanged.
tidybrreg 0.3.6
Bug fixes
-
field_dict:capital_shares(kapital.antallAksjer) is now typednumericinstead ofinteger. The share count exceeds the 32-bit integer range for large-cap entities (e.g. Equinor ASA, 2,556,807,512 shares), socoerce_types()silently producedNAwith an “NAs introduced by coercion to integer range” warning. It now retains the value, consistent with the otherkapital.*fields. - Bundled
role_typesandrole_groups: Norwegian role names (Observatør,Regnskapsfører,Forretningsfører,FFØR,Helse, miljø og sikkerhet) are now stored as UTF-8 escapes indata-raw/build_dictionaries.Rand saved with UTF-8 encoding marking. String values are byte-identical to before; this only clears the R CMD check “non-ASCII strings” data warning.
Internal
-
read_changelog(): the arrow branch now references the partition column as.data$sync_dateand imports therlang::.envpronoun, removing the “no visible binding for global variable” check note. Runtime behaviour is unchanged. - Tests:
field_dictinvariants aligned with the v0.3.5 dictionary —api_pathis the unique key (multiple API-spelling variants map to a singlecol_name), andnumericis an accepted type.
tidybrreg 0.3.4
Roller CDC: field-level change detection
-
bootstrap_state()now initializes the CDC cursor to the current tip (max event ID) at bootstrap time viaget_cdc_tip(). Previously the cursor remained at 0 after bootstrap, causing the first CDC poll to replay the entire event history (~4.4M roller events, ~24M enheter events). This caused OOM and timeout failures on Cloud Run. -
bootstrap_state(roller_method = "cdc")skips the roller totalbestand download entirely. Writes an empty state table and builds state incrementally via per-orgbrreg_roles()calls. Reduces bootstrap from 32 GiB RAM / 30+ minutes to <10 seconds and negligible memory. -
paginate_cdc()gains amax_pagesparameter and a safety guard: ifcursor_id == 0(no prior sync), pagination caps at 5 pages and emits acli::cli_warn(). Belt-and-suspenders — should never trigger after a correct bootstrap. -
diff_roller_state()(new, exported) — computes field-level diffs between two flattened roller state tibbles. Returns a long-format changelog withchange_type(entry/exit/change),field,value_from,value_to. Roles are keyed by a composite of(org_nr, role_group_code, role_code, holder_id)where holder_id is derived fromperson_id(person-held) orentity:{org_nr}(entity-held). -
brreg_sync(roller_method = "bulk")— new default strategy for roller CDC. Downloads the full totalbestand (~131 MB), diffs against stored state, and writes a field-level changelog. Replaces the per-org API pattern for full-register syncs. -
brreg_sync(roller_method = "cdc")— per-org API fallback for sub-daily syncs. Fetches current roles viabrreg_roles()for each CDC event, diffs per-org. Slower but provides per-event timestamp attribution. -
flatten_roles()gains 4 new columns:deregistered(avregistrert),ordering(rekkefolge),elected_by(valgtAv$kode),group_modified(sistEndret as Date). -
brreg_board_summary()now excludes resigned and deregistered roles from all counts and gainsn_employee_elected(count of board members with a non-NAelected_byvalue).
Performance
-
flatten_roles_bulk_fast()(internal) — vectorized two-pass flatten for bulk totalbestand. Pre-allocates vectors and fills by index. 4.1× faster than the per-entityflatten_roles()path (4,192 vs 1,028 roles/sec). -
read_roles_json()(internal) — dispatches to yyjsonr when available (10× parse speed, 70× lower memory vs jsonlite). yyjsonr added to Suggests. -
lookup_role_vec()andlookup_role_group_vec()(internal) — vectorized code-to-label lookups replacing per-rowmatch().
Bug fixes
-
extract_entity_name()no longer returns NA for entity-held roles. The brreg API returnsenhet.navnas a JSON array["ERNST & YOUNG AS"], not a named object. jsonlite parses this as an unnamed list, which the old code did not handle. Added unnamed list branch. -
read_roles_json()now decompresses.gzfiles to a temp file before passing to yyjsonr. yyjsonr cannot read gzipped files directly; the previous code crashed with a buffer allocation error on the 131 MB totalbestand. -
paginate_cdc_bounded()(internal) caps roller CDC pagination at 5 pages (50K events) when usingroller_method = "bulk". The previous unboundedpaginate_cdc()fetched the entire CDC history (1.1M+ events) from cursor 0 on first bootstrap, causing 30-minute timeouts. -
parse_sync_page()no longer produces tibble column size mismatches when CDC pages contain events withoutendringer(Ny/Sletting).raw_changes[[i]] <- NULLwas deleting list elements instead of preserving NULL placeholders (R double-bracket assignment semantics). Fix:list()as empty placeholder. Affected all enheter and underenheter sync since v0.3.2. -
add_role_key()no longer crashes on 0-row tibbles. Previously,case_when(df$person_id ...)received NULL instead of NA when passed a 0-column tibble from a 404 API response. -
apply_roller_events_cdc()skipsdiff_roller_state()when both old and new state are empty (entity not in state AND 404 from API).
tidybrreg 0.3.3
Bug fixes
-
brreg_update_fields()no longer silently drops Ny, Sletting, and Fjernet CDC events. Events with noendringerarray now emit a synthetic row withoperation = NA,field = NA,new_value = NA, preserving event metadata for downstream filtering and counting. Previously, filteringbrreg_update_fields()output forchange_type == "Ny"returned zero rows. -
flatten_page_patches()andparse_patch()now handle RFC 6902moveoperations correctly: the value is written to the destination path and a syntheticremoverow is emitted for the source path (from$from).copyoperations already worked but now follow the same explicit dispatch path. - Stale roxygen docstring for
brreg_update_fields()removed references to RcppSimdJson and parallel processing (both removed in 0.3.2). Documentation now accurately describes the sequential fetch-and-flatten loop and the synthetic row behaviour for Ny/ Sletting/Fjernet events.
Field dictionary
-
field_dictgrows from 62 to 70 rows. New entries:-
fravalgRevisjonDato→audit_exemption_date(Date) -
fravalgRevisjonBeslutningsDato→audit_exemption_decision_date(Date) -
registreringsdatoMerverdiavgiftsregisteretEnhetsregisteret→vat_registration_date_er(Date) -
registreringsdatoAntallAnsatteEnhetsregisteret→employee_reg_date_er(Date) -
registreringsdatoAntallAnsatteNavAaregisteret→employee_reg_date_nav(Date) -
oppstartsdato→start_date(Date) — underenhet operations start date -
registrertIPartiregisteret→in_party_register(logical) -
respons_klasse→response_class(character) — API response metadata class
-
Sync engine
-
find_state_column()gains mappings for all 8 new field_dict entries. CDC field changes for audit exemption dates, employee registration dates, VAT registration date in Enhetsregisteret, underenhet start dates, and party register membership are no longer silently skipped duringbrreg_sync().
tidybrreg 0.3.2
Event-sourcing sync engine
-
brreg_sync()— maintains a local mirror of the Enhetsregisteret by applying incremental CDC events to persistent parquet state files. On first run, bootstraps from bulk download. Subsequent runs poll from the last cursor position. Write ordering (changelog → state → cursor) ensures crash-safe idempotent replay. -
brreg_sync_status()— displays state file sizes, cursor positions, last sync time, and changelog partition count. - Four state files maintained:
enheter.parquet,underenheter.parquet,roller.parquet,paategninger.parquet. - Hive-partitioned changelog under
state/changelog/sync_date=.../for efficient date-range queries viaarrow::open_dataset().
Registry annotations (påtegninger)
-
brreg_annotations()— query the påtegninger state table by org_nr and/or infotype code. Påtegninger are registry-level annotations about entity data quality — the earliest formal signal of entity distress, preceding forced dissolution by weeks to months. -
brreg_annotation_summary()— count entities with active annotations grouped by infotype. - Påtegninger treated as a conceptually distinct fourth data stream alongside enheter, underenheter, and roller.
Unified change tracking
-
brreg_changes()— query the changelog for field-level mutations across all four streams. Filter by track (field names), registry, change_type, date range, and org_nr. -
brreg_change_summary()— count changes by registry, type, field. -
brreg_flows()now auto-detects the changelog when called with no arguments:brreg_flows()reads from the sync changelog,brreg_flows(data)uses the original bulk + CDC path.
tidybrreg 0.3.1
New functions
-
brreg_network()— build entity ego-network graphs astbl_graphobjects. Depth 0 (seed only), depth 1 (sub-units, children, roles, legal roles via API), depth 2 (board interlocks via local bulk data). Extensible collector pattern for future relationship types. -
brreg_underenheter()— convenience wrapper to get all sub-units (BEDR/AAFY) belonging to a parent entity. -
brreg_children()— get child enheter in the organisational hierarchy (e.g. Stortinget → Riksrevisjonen). -
brreg_status()— check local bulk data availability for all three registry types.
Changes
-
brreg_entity()now defaults toregistry = "auto", trying enheter first then falling back to underenheter on 404. Output gains aregistrycolumn. Explicitregistry = "enheter"orregistry = "underenheter"skips the fallback. - Bulk data resolution uses Arrow lazy-load for all three types (was roller-only). Session cache in
.brregEnvavoids re-reading parquet files across repeated calls. Per-type lazy pipeline in depth-2 expansion early-exits when no new entities are discovered.
tidybrreg 0.3.0
Documentation
- pkgdown site with 10 reference groups deployed to GitHub Pages.
- 5 vignettes: Getting started, Norwegian business data, Building firm panels, Corporate governance research, Package architecture.
- ARCHITECTURE.md (390 lines) documenting full data flow.
- CONTRIBUTING.md, CODE_OF_CONDUCT.md, GitHub issue templates.
- Hex sticker logo at
man/figures/logo.svg. - Lifecycle experimental badge in README.
- r-universe registration for binary installs.
- Install instructions updated: pak (recommended), r-universe, remotes.
tidybrreg 0.2.0
New features
Snapshot engine
-
brreg_snapshot()downloads and saves dated bulk register extracts as Hive-partitioned Parquet files. Supportstype = "enheter","underenheter", and"roller"(via/roller/totalbestand). Raw.gzfiles are preserved alongside processed Parquet for provenance. -
brreg_import()adds user-supplied historical CSVs as snapshot partitions, normalizing column names viafield_dict. -
brreg_snapshots()lists available snapshots with dates, sizes, and paths. -
brreg_open()opens the partitioned dataset as a lazy Arrow Dataset (requires the arrow package). -
brreg_data_dir()returns the snapshot store path. Override withoptions(brreg.data_dir = "/custom/path"). -
brreg_cleanup()prunes old partitions by count or age.
Provenance manifest
-
brreg_manifest()reads the JSON provenance catalog recording every download: endpoint URL, download timestamp,Last-Modifiedheader (data vintage date), ETag, file hash, record count, and file paths. Used to bridge snapshots to CDC updates without gaps.
Panel construction
-
brreg_panel()constructs firm x period panels at annual, quarterly, monthly, or custom cadence from accumulated snapshots. Uses LOCF (last observation carried forward) date resolution. -
brreg_replay()reconstructs register state at arbitrary dates from a base snapshot + CDC update stream. Applies Ny/Endring/Sletting events chronologically viadplyr::rows_upsert()pattern. -
brreg_events()diffs two snapshots: entries, exits, field changes with both old and new values. -
brreg_series()computes aggregate time series for any combination of variables (.vars), summary functions (.fns), and grouping columns (by). Output columns named{variable}_{function}. -
as_brreg_tsibble()converts panel or series output to tsibble withregular = FALSEfor the tidyverts ecosystem.
Entity and sub-entity access
-
brreg_entity()andbrreg_search()gainregistry = "underenheter"parameter for sub-entity (establishment) lookups and search. -
brreg_roles_legal()performs reverse role lookup: what roles does entity X hold in other entities (parent, shareholder, partner).
Bulk downloads
-
brreg_download()supportstype = "roller"via/roller/totalbestand(131 MB gzipped JSON). -
brreg_download(format = "json")downloads the full JSON bulk for enheter/underenheter via/enheter/lastned. - Algorithmic JSON unnesting flattens all list columns to atomic types (character vectors collapsed, data frames serialized, HAL links dropped). Shared
rename_and_coerce()pipeline for both CSV and JSON paths.
Concordance
-
brreg_harmonize_kommune()remaps municipality codes across Norway’s 2020 municipal reform and 2024 county reversals using SSB KLASS. -
brreg_harmonize_nace()remaps NACE SN2007 to SN2025 via SSB KLASS.
Governance research
-
brreg_board_network()builds director interlock networks astbl_graphobjects (requires tidygraph). -
brreg_survival_data()prepares firm survival data with time-to-event and right-censoring indicators compatible withsurvival::Surv().
tidybrreg 0.1.0
- Initial release.
- Entity lookup (
brreg_entity()), filtered search (brreg_search()), board/officer roles (brreg_roles(),brreg_board_summary()). - Full register bulk download (
brreg_download()). - Incremental CDC updates (
brreg_updates()). - Code-to-label translation (
brreg_label(),get_brreg_dic()). - Organization number validation (
brreg_validate()). - Reference datasets:
field_dict,legal_forms,role_types,role_groups.