HWHeat Waves
    DashboardUtforsk
    Analyse
    Data Kilder
        • Category Codes
        • Filter Axes
        • Regions
        • Building Types
        • SSB Tables
        • API Examples
      • Catalog
      • Pipeline
      • Entrypoints
    • Design Rationale
    • Doc Map
    DocsSettings
    DashboardAtlasUtforsk
    Analyse
    Data Kilder
    1. Documentation
    2. Ingestion
    3. Adapter Mappings
    4. Source Api Request Examples

    Loading documentation...

    Native API Comparison — Smoke Test Datasets

    Side-by-side reference of how the native datasource APIs look for a representative set of HeatWaves requests. Datasets 1–11 cover the matrix for BRIS/BRASK smoke tests (dataset 12 is an extra BRIS case). Each example pairs the HeatWaves intent with the exact upstream request body or form. Together with category-native-to-cat-code.md and filter-axes-native-to-filter-id.md, these eleven examples are designed to be sufficient for any HeatWaves ETL developer to compose a working native request for any (datasource, dimension, filter) combination supported by the platform.

    Every numeric id, parameter name, and value below is sourced from one of:

    • category-native-to-cat-code.md — cat_code ↔ native_api_code mapping
    • filter-axes-native-to-filter-id.md — filter parameter names and values per source
    • lib/data-model/adapters/_brisRequestBody.ts — BRIS request builder (buildBrisRequestBody)
    • lib/data-model/adapters/_brisUpstream.constants.ts — BOLIG_IDS, NON_RESIDENTIAL_MATRIKKEL_LEAF_IDS, BRIS_QUESTION_AGGREGATIONS, TIME_OPTIONS
    • lib/data-model/adapters/brask-passthrough.ts — BRASK form mechanics (shared constants in lib/data-model/adapters/_braskWebForms.constants.ts: BRASK_FILTER_FIELD_MAP, BRASK_AXIS / braskExplorerAxisId, BRASK_UTFORSK_RBL_*)
    • ssb-pxwebapi-tables.md — SSB table 12058 variable reference

    Unless otherwise noted, every example is a time series with years on the x-axis, antall (or kr) on the y-axis, and categories within the chosen dimension as series.

    Filter vocabulary in HeatWaves intent lines — per filter-axes-native-to-filter-id.md: — = axis omitted / unfiltered upstream slice (NULL *_code FK on facts; no filter-row standard_code). RESIDENTIAL, DEVELOPED, etc. = resolved filters.standard_code. none / n/a = axis not available on that upstream endpoint. Do not use ALL or ALL_BUILDINGS here — those codes belong on categories (cross-tab dimension totals), not the filter axes.


    Shared API Mechanics

    Three upstream endpoints are exercised across the eleven datasets:

    SourceEndpointBody type
    BRASKPOST https://brask.finansnorge.no/application/x-www-form-urlencoded (ASP.NET form)
    BRIS — Alle branner og ulykkerPOST https://brannstatistikk.no/api/v1/missionreports/aggregateapplication/json (full filter body)
    BRIS — Hvordan bygningsbranner starterPOST https://brannstatistikk.no/api/v1/missionreports/restrictedaggregationapplication/json (minimal body)
    SSBPOST https://data.ssb.no/api/v0/no/table/12058application/json (PxWebApi v0 query, returns json-stat2)

    All BRASK requests must first GET https://brask.finansnorge.no/ and include the __VIEWSTATE, __VIEWSTATEGENERATOR, and __EVENTVALIDATION hidden fields from the response. Those three fields are omitted from every example below for readability.


    Dataset 1 — Police, Arnested, Utviklet brann, Bolig

    HeatWaves intent

    • datasource: BRIS — Police (Sub-A, "Alle branner og ulykker")
    • dimension: dim_code = 4 (Arnested) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = RESIDENTIAL

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/aggregate Content-Type: application/json
    { "hitsToReturn": 0, "skipped": 0, "missionTypes": { "ids": [], "isMissingValue": false }, "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "monthNumbers": { "ids": [], "isMissingValue": false }, "dayOfWeek": { "ids": [], "isMissingValue": false }, "responseTimeLimitGreaterThan": true, "callOutTimeLimitGreaterThan": true, "effortDurationGreaterThan": true, "counties": { "ids": [], "isMissingValue": false }, "municipalities": { "ids": [], "isMissingValue": false }, "basicStatisticalUnits": { "ids": [], "isMissingValue": false }, "fireDepartments": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "streetAddress": "", "geoShapeFilter": { "polygons": [], "circles": [] }, "fireStations": { "ids": [], "isMissingValue": false }, "dispatchedFireDepartments": { "ids": [], "isMissingValue": false }, "buildingTypes": { "ids": [ "111","112","113","121","122","123","124","131","133","135","136", "141","142","143","144","145","146","151","152","159","161","162", "163","171","172","181","182","183","193","199" ], "isMissingValue": false }, "naceCodes": { "ids": [], "isMissingValue": false }, "includeAssistanceMissions": false, "includeExercises": false, "includeMissionsHandledByHundredAndTen": false, "includePoliceCauseWithNoMission": false, "includeRVRMissionsFromBris": false, "includeApprovedMissions": true, "onlyApprovedMissions": true, "answers": [], "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "403", "name": "Arnested - politi", "type": "TERMS", "interval": null, "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — aggregationDefinition.id = "403" is Police-only on /aggregate. HeatWaves ingest sends one Matrikkel leaf id per slice (buildingTypeIdsOverride); the 30-id Bolig body below is the upstream shape for a manual rollup request only — see filter-axes-native-to-filter-id.md. Leaf reference list: BOLIG_IDS in _brisUpstream.constants.ts. Result rows are keyed by 403_* answer ids — see category-native-to-cat-code.md §4 (cat_codes 401-421).


    Dataset 2 — DSB, Arnested, Utviklet brann, no building type

    HeatWaves intent

    • datasource: BRIS — DSB ("Hvordan bygningsbranner starter")
    • dimension: dim_code = 4 (Arnested) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = none (not supported on this endpoint)

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/restrictedaggregation Content-Type: application/json
    { "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "counties": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "includePoliceCauseWithNoMission": false, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "227", "name": "Antatt arnested (startsted)", "type": "TERMS", "interval": null, "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — aggregationDefinition.id = "227" is restrictedOnly. The restricted body has no buildingTypes field at all (see filter-axes-native-to-filter-id.md §2 — Building Type is unsupported on /restrictedaggregation). Result rows are keyed by 227_* answer ids — category-native-to-cat-code.md §4 (cat_codes 422-448).


    Dataset 3 — Police, Objekt, Utviklet brann, Bolig

    HeatWaves intent

    • datasource: BRIS — Police (Sub-A)
    • dimension: dim_code = 3 (Objekt) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = RESIDENTIAL

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/aggregate Content-Type: application/json

    Same envelope as Dataset 1 with two changes:

    { "...": "[same as Dataset 1, except:]", "buildingTypes": { "ids": ["...30 BOLIG_IDS..."], "isMissingValue": false }, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "405", "name": "Brannen startet i - politi", "type": "TERMS", "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — aggregationDefinition.id = "405" is fullOnly. Result rows are keyed by 405_* answer ids — category-native-to-cat-code.md §3 (cat_codes 301-322).


    Dataset 4 — Police, Objekt, Utviklet brann, all buildings

    HeatWaves intent

    • datasource: BRIS — Police (Sub-A)
    • dimension: dim_code = 3 (Objekt) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = — (unfiltered — all buildings)

    Native API

    Identical to Dataset 3 except buildingTypes.ids is empty:

    { "...": "[same as Dataset 3, except:]", "buildingTypes": { "ids": [], "isMissingValue": false } }

    Notes — Compare side-by-side with Dataset 3 to see the building-type filter on/off delta cleanly. Empty buildingTypes.ids array means "all building types" per filter-axes-native-to-filter-id.md §2.


    Dataset 5 — DSB, Tennkilde, Begrenset branntilløp, all buildings

    HeatWaves intent

    • datasource: BRIS — DSB ("Hvordan bygningsbranner starter")
    • dimension: dim_code = 2 (Tennkilde) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = CONFINED, building_type = none (not supported on this endpoint)

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/restrictedaggregation Content-Type: application/json
    { "revisedMissionTypes": { "ids": [10086, 10212], "isMissingValue": false }, "counties": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "includePoliceCauseWithNoMission": false, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "264", "name": "Antatt tennkilde", "type": "TERMS", "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — revisedMissionTypes.ids = [10086, 10212] covers Branntilløp komfyr + Branntilløp i bygg annet. Brann i skorstein (10175) is omitted here — it does not exist in the restricted "Hvordan bygningsbranner starter" dataset (see filter-axes-native-to-filter-id.md §1). Result rows are keyed by 264_* — category-native-to-cat-code.md §2 (cat_codes 208-213).


    Dataset 6 — BRASK, Tennkilde, Begrenset, Bolig, Oslo, alder 1–5, value=kr

    HeatWaves intent

    • datasource: BRASK
    • dimension: dim_code = 2 (Tennkilde / Kilde) — series
    • x-axis: year
    • y-axis: erstatningsbeløp (kr)
    • filters: fire_type = CONFINED, building_type = RESIDENTIAL, region = Oslo, building_age = 1-5

    This is the most filter-heavy BRASK example — exercises every BRASK filter mechanic in one request.

    Native API

    POST https://brask.finansnorge.no/ Content-Type: application/x-www-form-urlencoded
    __VIEWSTATE=<from initial GET>
    __VIEWSTATEGENERATOR=<from initial GET>
    __EVENTVALIDATION=<from initial GET>
    ctl00$Innhold$ddlRad=5
    ctl00$Innhold$ddlKolonne=7
    ctl00$Innhold$rblVerdi=2
    ctl00$Innhold$rblBeregning=1
    ctl00$Innhold$lbBransje=
    ctl00$Innhold$lbAldersgruppe=2
    ctl00$Innhold$lbFylke=3
    ctl00$Innhold$lbDag=
    ctl00$Innhold$lbKilde=
    ctl00$Innhold$lbKvartal=
    ctl00$Innhold$lbMåned=
    ctl00$Innhold$lbNæring=0
    ctl00$Innhold$lbType=2
    ctl00$Innhold$lbUkedag=
    ctl00$Innhold$lbÅr=
    ctl00$Innhold$lbÅrsak=
    ctl00$Innhold$btnKjør=Lag tabell
    

    Notes — Field-by-field justification:

    • ddlRad=5 — row dimension = kilde (Utforsk ddlRad index = BRASK_AXIS.kilde (5) in _braskWebForms.constants.ts; not fire_data.categories.dim_code).
    • ddlKolonne=7 — column dimension = ar (BRASK_AXIS.ar = 7).
    • rblVerdi=2 — value type = erstatningsbelop (kr); 1 would be antallSkader.
    • lbAldersgruppe=2 — native value 2 = "1-5" per filter-axes-native-to-filter-id.md §3 (normalizes to HeatWaves bucket 0-5).
    • lbFylke=3 — Oslo per filter-axes-native-to-filter-id.md §4.
    • lbNæring=0 — Beboelse = RESIDENTIAL per filter-axes-native-to-filter-id.md §2.
    • lbType=2 — Kald = CONFINED per filter-axes-native-to-filter-id.md §1.
    • All other lb* fields sent empty (= Alle / no filter).

    Dataset 7 — Brigade, Årsak, Komfyr-only confined, all buildings

    HeatWaves intent

    • datasource: BRIS — Fire Brigade ("Hvordan bygningsbranner starter")
    • dimension: dim_code = 1 (Årsak) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = CONFINED — komfyr only, building_type = none

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/restrictedaggregation Content-Type: application/json
    { "revisedMissionTypes": { "ids": [10086], "isMissingValue": false }, "counties": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "includePoliceCauseWithNoMission": false, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "269", "name": "Brannvesenets vurdering", "type": "TERMS", "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — Single-mission-type slice; the narrowest fire-type filter possible on BRIS. Useful as an ETL ingestion canary because the row count is small and easy to verify by hand. aggregationDefinition.id = "269" is restrictedOnly. Result rows are keyed by 269_* — category-native-to-cat-code.md §1 (cat_codes 117-122).


    Dataset 8 — BRASK, Årsak, all fire types, all buildings, Oslo, value=kr

    HeatWaves intent

    • datasource: BRASK
    • dimension: dim_code = 1 (Årsak) — series
    • x-axis: year
    • y-axis: erstatningsbeløp (kr)
    • filters: fire_type = — (unfiltered), building_type = — (unfiltered), region = Oslo

    Native API

    POST https://brask.finansnorge.no/ Content-Type: application/x-www-form-urlencoded
    __VIEWSTATE=<from initial GET>
    __VIEWSTATEGENERATOR=<from initial GET>
    __EVENTVALIDATION=<from initial GET>
    ctl00$Innhold$ddlRad=6
    ctl00$Innhold$ddlKolonne=7
    ctl00$Innhold$rblVerdi=2
    ctl00$Innhold$rblBeregning=1
    ctl00$Innhold$lbBransje=
    ctl00$Innhold$lbAldersgruppe=
    ctl00$Innhold$lbFylke=3
    ctl00$Innhold$lbDag=
    ctl00$Innhold$lbKilde=
    ctl00$Innhold$lbKvartal=
    ctl00$Innhold$lbMåned=
    ctl00$Innhold$lbNæring=
    ctl00$Innhold$lbType=
    ctl00$Innhold$lbUkedag=
    ctl00$Innhold$lbÅr=
    ctl00$Innhold$lbÅrsak=
    ctl00$Innhold$btnKjør=Lag tabell
    

    Notes

    • ddlRad=6 — row dimension = arsak (BRASK_AXIS.arsak = 6 in _braskWebForms.constants.ts).
    • ddlKolonne=7 — column dimension = ar (year).
    • rblVerdi=2 — kr.
    • lbFylke=3 — Oslo only.
    • lbType= and lbNæring= empty — all fire types / all building types.
    • The only example showing kr value-type without fire-type or building-type narrowing — useful as a "broad financial loss by cause" smoke test.

    Dataset 9 — BRIS Police, Arnested, Utviklet brann, all buildings, Oslo

    HeatWaves intent

    • datasource: BRIS — Police (Sub-A)
    • dimension: dim_code = 4 (Arnested) — series
    • x-axis: year
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = — (unfiltered — all buildings), region = Oslo

    Direct cross-source counterpart to Datasets 6 and 8 — shows how Oslo is filtered on the BRIS side.

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/aggregate Content-Type: application/json
    { "...": "[same as Dataset 1 envelope, except:]", "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "counties": { "ids": [3], "isMissingValue": false }, "buildingTypes": { "ids": [], "isMissingValue": false }, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "403", "name": "Arnested - politi", "type": "TERMS", "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_YEAR", "name": "År", "type": "DATE_HISTOGRAM", "interval": "YEAR", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Notes — cross-system caveat

    • BRASK lbFylke = 3 and BRIS counties.ids = [3] both happen to equal Oslo, but the systems align only by coincidence for Oslo. Every other county diverges:
      • BRASK uses pre-2020 cresta-sone (e.g. 2 = Akershus, 7 = Vestfold, 19 = Troms).
      • BRIS uses SSB klass 104 (e.g. 30 = Viken for 2020-2023 incidents, 32 = Akershus for post-2024 incidents — see fylke-code appendix below).
    • For finer granularity, municipalities.ids = [301] would also work for Oslo in BRIS.

    Dataset 10 — SSB, Boligbranner (RESIDENTIAL dataset), Oslo

    HeatWaves intent

    • datasource: SSB (third datasource — first appearance in this doc)
    • dimension: total counts (no within-dimension series — SSB does not break down causal/object dimensions)
    • x-axis: year
    • y-axis: antall
    • filters: building_type = RESIDENTIAL (encoded by selecting the Boligbranner statistical variable, not a parameter), region = Oslo

    Cross-source counterpart to Datasets 6, 8, 9 — same Oslo geography across all four sources.

    Native API

    POST https://data.ssb.no/api/v0/no/table/12058 Content-Type: application/json
    { "query": [ { "code": "KOKkommuneregion0000", "selection": { "filter": "item", "values": ["0301"] } }, { "code": "ContentsCode", "selection": { "filter": "item", "values": ["KOSboligbranner0000"] } }, { "code": "Tid", "selection": { "filter": "top", "values": ["10"] } } ], "response": { "format": "json-stat2" } }

    Notes

    • Table 12058 is the SSB KOSTRA fire and safety table. Variable codes are documented in ssb-pxwebapi-tables.md.
    • For the all-buildings total slice (building_type_code NULL; Bygningsbranner / KOSbygningsbrann0000) instead of RESIDENTIAL, swap the ContentsCode value accordingly. For kr instead of antall: KOSerstatnbygnin0000.
    • KOKkommuneregion0000: municipality codes (0301 = Oslo) or EAK for national total. PxWeb → region_code: filter-axes-native-to-filter-id.md §4; upstream codes: ssb-pxwebapi-tables.md.
    • Tid is years natively in SSB — no equivalent of BRIS's nextLevelAggregationDefinition is needed. The "top": "10" value means "the most recent 10 years"; replace with { "filter": "item", "values": ["2020","2021","2022","2023","2024"] } for an explicit list.
    • Response format json-stat2: row-major value array — decode contract in pipeline-slices-idempotency-and-triggers.md §4.3; PxWebApi mechanics in ssb-pxwebapi-tables.md.

    Dataset 11 — BRIS DSB, Tennkilde, no time sub-aggregation

    HeatWaves intent

    • datasource: BRIS — DSB
    • dimension: dim_code = 2 (Tennkilde) — series
    • x-axis: none (categories only — no time series)
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = none

    This is the only example in the doc that does NOT match the "year on x-axis" rule — included expressly to show the simpler payload shape for ETL slices that only need categorical totals.

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/restrictedaggregation Content-Type: application/json
    { "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "counties": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "includePoliceCauseWithNoMission": false, "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "264", "name": "Antatt tennkilde", "type": "TERMS", "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } }

    Notes — Identical to Dataset 5 with two changes: revisedMissionTypes.ids = [10026] (DEVELOPED), and nextLevelAggregationDefinition is omitted entirely from the aggregationDefinition object. The response shape is then a flat result[0].buckets[<category>].count map (no time sub-buckets); the BRIS adapter parses that into fact_yearly rows for /explore charts via get_fire_data.


    Dataset 12 — BRIS Police, Arnested, Utviklet brann, DAY granularity, single year window

    HeatWaves intent

    • datasource: BRIS — Police (Sub-A, "Alle branner og ulykker")
    • dimension: dim_code = 4 (Arnested) — series
    • x-axis: date (one bucket per calendar day)
    • y-axis: antall
    • filters: fire_type = DEVELOPED, building_type = none, period = 2024

    This is the canonical day-grain BRIS request used by the daily ingest adapter (lib/data-model/adapters/bris.daily.ts). Tier-2 sends the same body with interval: "MONTH" per drifted year; Tier-3 sends this body with the period narrowed to a single (year, month).

    Native API

    POST https://brannstatistikk.no/api/v1/missionreports/aggregate Content-Type: application/json
    { "hitsToReturn": 0, "skipped": 0, "missionTypes": { "ids": [], "isMissingValue": false }, "revisedMissionTypes": { "ids": [10026], "isMissingValue": false }, "monthNumbers": { "ids": [], "isMissingValue": false }, "dayOfWeek": { "ids": [], "isMissingValue": false }, "responseTimeLimitGreaterThan": true, "callOutTimeLimitGreaterThan": true, "effortDurationGreaterThan": true, "counties": { "ids": [], "isMissingValue": false }, "municipalities": { "ids": [], "isMissingValue": false }, "basicStatisticalUnits": { "ids": [], "isMissingValue": false }, "fireDepartments": { "ids": [], "isMissingValue": false }, "hundredAndTenCentrals": { "ids": [], "isMissingValue": false }, "streetAddress": "", "geoShapeFilter": { "polygons": [], "circles": [] }, "fireStations": { "ids": [], "isMissingValue": false }, "dispatchedFireDepartments": { "ids": [], "isMissingValue": false }, "buildingTypes": { "ids": [], "isMissingValue": false }, "naceCodes": { "ids": [], "isMissingValue": false }, "includeAssistanceMissions": false, "includeExercises": false, "includeMissionsHandledByHundredAndTen": false, "includePoliceCauseWithNoMission": false, "includeRVRMissionsFromBris": false, "includeApprovedMissions": true, "onlyApprovedMissions": true, "answers": [], "periodStart": "2024-01-01", "periodEnd": "2024-12-31", "aggregationQuery": { "includeNationalFigures": false, "aggregationDefinition": { "id": "403", "name": "Arnested - politi", "type": "TERMS", "interval": null, "question": true, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [], "nextLevelAggregationDefinition": { "id": "MISSION_CALL_TIME_DAY", "name": "Dag", "type": "DATE_HISTOGRAM", "interval": "DAY", "question": false, "groupAnswer": false, "restricted": false, "includeMissing": true, "calculatedSubAggregationDefinitions": [] } } } }

    Response shape — verified live

    The full raw response is committed at .live_responses/dataset_12_police_arnested_DAY_2024.json. Key invariants the daily adapter relies on:

    • result[0].buckets[<answer_id>].subAggregationBuckets keys are Unix-ms timestamps that decode via new Date(Number(ts)).toISOString().slice(0, 10) to ISO dates (UTC midnight, no timezone offset).
    • result[0].total === Σ inner.count across every category × day bucket (3244 / 3244 in the sample).
    • BRIS zero-pads every calendar day in the period — the 2024 sample has 366 unique day buckets per category (leap year), most with count: 0. transformBrisDailyResponse skips zero cells the same way transformBraskDailyTable does.
    • The -1 bucket ("Ikke besvart") resolves through resolveCatCodeFromBrisBucket (native_name = 'Ikke besvart' in the catalog).

    Notes — Same body as Dataset 1 with buildingTypes.ids = [], nextLevelAggregationDefinition.interval = "DAY", and explicit periodStart / periodEnd. Switching interval to "MONTH" is the Tier-2 monthly-probe variant of the same body.


    Norwegian Fylke Code Reference

    Authoritative codes for BRIS counties.ids come from SSB klass 104 (Standard for fylkesinndeling). Post-2024 incidents (from 2024-01-01): BRIS uses the same numeric ids as the HeatWaves canonical county spine — one table only, in regions-and-cresta.md (section “Canonical region set (2024, county level)”). Do not copy that table here; update the canonical doc when the spine changes.

    Pre-2024 (incidents 2020-01-01 to 2023-12-31) — BRIS klass 104 historical fylke ids for request bodies only (differs from the 2024 canonical list):

    CodeFylke
    03Oslo
    11Rogaland
    15Møre og Romsdal
    18Nordland
    30Viken
    34Innlandet
    38Vestfold og Telemark
    42Agder
    46Vestland
    50Trøndelag
    54Troms og Finnmark
    99Uoppgitt

    ETL caveat — BRIS returns historical incidents under the codes that were valid at the time of the incident. A query that filters counties.ids = [30] (Viken) only matches incidents from 2020-2023; post-2024 incidents in the same geography are split across [31, 32, 33]. HeatWaves should normalize across both code sets when building cross-year region series. Source: https://data.ssb.no/api/klass/v1/classifications/104/codes.


    Summary Matrix

    #DatasourceDimensionFire typeBuilding typeRegionEndpointBody type
    1Police (Sub-A)ArnestedDEVELOPEDRESIDENTIAL—/aggregatefull JSON
    2DSBArnestedDEVELOPEDn/a—/restrictedaggregationminimal JSON
    3Police (Sub-A)ObjektDEVELOPEDRESIDENTIAL—/aggregatefull JSON
    4Police (Sub-A)ObjektDEVELOPED——/aggregatefull JSON, empty buildingTypes.ids
    5DSBTennkildeCONFINED (no skorstein)n/a—/restrictedaggregationminimal JSON
    6BRASKTennkildeCONFINEDRESIDENTIALOsloform POSTURL-encoded form, value=kr, age 1-5
    7BrigadeÅrsakCONFINED komfyr-onlyn/a—/restrictedaggregationminimal JSON, single mission
    8BRASKÅrsak——Osloform POSTURL-encoded form, value=kr
    9Police (Sub-A)ArnestedDEVELOPED—Oslo/aggregatefull JSON, counties.ids=[3]
    10SSB(totals)n/aRESIDENTIAL via datasetOslo (0301)PxWebApi v0JSON-stat2 query
    11DSBTennkildeDEVELOPEDn/a—/restrictedaggregationminimal JSON, no time sub-agg
    12Police (Sub-A)ArnestedDEVELOPED——/aggregatefull JSON, interval: DAY, periodStart/periodEnd

    Live API Smoke Test Results (for validation)

    Every native request above was executed by scripts/smoke_api_comparison.mjs. Full raw responses are saved under .live_responses/ (one file per dataset — .json for BRIS/SSB, .html for BRASK). That directory is tracked in git so documentation links to pinned samples stay valid in fresh clones; re-run the smoke script when upstream responses or slice templates change, then commit refreshed files. The summary below lets you cross-check key totals against the source websites without opening the raw files.

    Smoke-test environment

    • Run command: node scripts/smoke_api_comparison.mjs
    • All 12 calls returned HTTP 200.
    • BRIS year coverage: 2016–2026 (11 buckets, current data range of brannstatistikk.no).
    • BRASK year coverage: 1985–2025 (41 columns, full archive range).
    • SSB year coverage requested: top 10 (2016–2025).
    • The label -1 in BRIS results is the "missing answer" bucket returned because aggregationDefinition.includeMissing = true. Subtract it from grandTotal to get the answered-only count.

    Cross-source sanity checks

    These three identities hold across the responses and confirm the slice logic in filter-axes-native-to-filter-id.md is internally consistent:

    • Dataset 2 (DSB Arnested DEVELOPED) grandTotal = 32,320 = Dataset 4 (Police Objekt DEVELOPED, all buildings) grandTotal = Dataset 11 (DSB Tennkilde DEVELOPED, no sub-agg) grandTotal. All three slice the same population (DEVELOPED Brann i bygning nationwide, 2016–2026).
    • Dataset 1 (Police Arnested DEVELOPED, RESIDENTIAL) grandTotal = 15,879 = Dataset 3 (Police Objekt DEVELOPED, RESIDENTIAL) grandTotal. Same building-type filter, same mission slice.
    • Dataset 9 (Police Arnested DEVELOPED, Oslo, all buildings) grandTotal = 3,930 — strictly less than Dataset 1's 15,879. Oslo's share is roughly 25% of national, which matches the public Oslo brannvesen statistics for the period.

    BRIS results (datasets 1, 2, 3, 4, 5, 7, 9, 11, 12)

    #EndpointQuestion idMission typesCatsGrand totalYear rangeTop 3 (cat → count)
    1/aggregate403 (Arnested - politi)[10026]2115,8792016–2026-1 → 10,092 · Kjøkken → 1,055 · Stue → 930
    2/restrictedaggregation227 (Antatt arnested)[10026]2832,3202016–2026Kjøkken → 4,708 · Stue → 3,996 · Utvendig → 3,897
    3/aggregate405 (Brannen startet i - politi)[10026]2315,8792016–2026-1 → 10,151 · Ukjent → 1,444 · Løst elektrisk utstyr → 842
    4/aggregate405[10026]2332,3202016–2026-1 → 21,074 · Ukjent → 2,735 · Løst elektrisk utstyr → 1,515
    5/restrictedaggregation264 (Antatt tennkilde)[10086, 10212]731,3922016–2026-1 → 31,313 · Elektrisitet → 48 · Annen → 14
    7/restrictedaggregation269 (Brannvesenets vurdering)[10086]417,8012016–2026-1 → 17,751 · Feil bruk → 46 · Ukjent → 3
    9/aggregate403[10026] + counties=[3]223,9302016–2026-1 → 2,176 · Kjøkken → 360 · Stue → 229
    11/restrictedaggregation264, no time sub-agg[10026]732,320n/a (categories-only)Ukjent → 10,940 · Elektrisitet → 7,539 · Åpen ild → 6,530
    12/aggregate403, interval: DAY, period 2024[10026]223,2442024-01-01..2024-12-31 (366 day buckets)-1 → 1,683 · Ukjent → 211 · Stue → 210

    Dataset 5 has very few non--1 answers (79 of 31,392) because Tennkilde (264) is rarely filled in for confined incidents. Dataset 7 likewise has only 50 non--1 answers because Brigade-vurdering (269) is rarely populated for Branntilløp komfyr events.

    BRASK results (datasets 6, 8)

    BRASK numbers are reported in tusen kr (thousands of NOK) when rblVerdi=2. The "Grand total" column below is the SUM × SUM cell of the returned table (sum across all categories AND all years).

    #Row dimFiltersCatsYear rangeGrand total (tusen kr)Sample row totals
    6kilde (5)Kald + Bolig + Oslo + 1–5 år + kr71985–20252,042Ildsted → 18 · Varme arbeider → 0 · Åpen ild → 0 · Elektroniske apparater → 60 · Husholdning → 166
    8arsak (6)Oslo + kr (no fire-type / building-type filter)71985–202514,554,292Antatt påsatt → 706,655 · Menneskelig feil → 1,725,625 · Teknisk svikt → 1,257,457 · Selvantennelse → 351,404 · Lynnedslag → 231,198

    Dataset 6's filters are narrow enough (Oslo + 1–5 år + Kald + Bolig) that the entire 41-year dataset only sums to ~2 million NOK in claims — verify on the BRASK UI by setting Aldersgruppe = 1–5, Fylke = Oslo, Type = Kald, Næring = Beboelse, Verdi = Erstatningsbeløp. The mostly-zero year columns are accurate. Dataset 8 returns 7 Årsak rows = the seven canonical BRASK cause categories from category-native-to-cat-code.md §1 (cat_codes 101–107).

    SSB results (dataset 10)

    SSB Boligbranner for Oslo (region 0301), 2016–2025, raw value array from json-stat2:

    YearAntall boligbranner
    2016265
    2017214
    2018247
    2019228
    2020238
    2021272
    2022204
    2023219
    2024250
    2025289
    Total 2016–20252,426

    SSB numbers are independent of BRIS/BRASK (different methodology — SSB sources from KOSTRA municipal reporting). For 2016–2025 SSB reports 2,426 boligbranner in Oslo. Compare to BRIS Dataset 9 (Police Arnested, Oslo, DEVELOPED, all buildings, 2016–2026) which reports 3,930 incidents — the BRIS number is larger because it includes non-RESIDENTIAL buildings, the -1 missing bucket, and one extra year.

    How to validate manually

    SourceManual check
    BRISOpen https://brannstatistikk.no/search, build the same query in the UI (mission type → group by → time), and compare the displayed counts.
    BRASKOpen https://brask.finansnorge.no/, set the same Rad/Kolonne dropdowns and filter checkboxes (Type, Næring, Aldersgruppe, Fylke), and compare the table.
    SSBOpen https://www.ssb.no/statbank/table/12058/, select region = Oslo, statistikkvariabel = Antall boligbranner, år = last 10, and compare the values.

    Re-run with node scripts/smoke_api_comparison.mjs. Raw responses overwrite the files in .live_responses/ so you can diff before/after on subsequent runs.