{
  "openapi": "3.1.0",
  "info": {
    "title": "ketl Unified API",
    "description": "Norsk åpen-data- og compliance-infrastruktur eksponert som ett API.\n\n**To rute-tier:**\n\n- `/api/data/{kilde}/{endepunkt}` — eksisterende proxy mot\n  `ketl-innsiktsmaskin-service` (~60 kilder, snake_case-passthrough).\n  Stabilt for v0.x, vil bli markert deprecated når Unified API når v1.\n- `/api/v1/*` — Unified API (#408). Domene-normalisert (`Company`,\n  `Source`, `Snapshot`, `Event`), kilde-agnostisk. Under bygging.\n\nSe [docs/DATA_PROXY.md](https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/blob/main/docs/DATA_PROXY.md)\nfor proxy-mønster og [docs/connectors/](https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/tree/main/docs/connectors)\nfor kilde-spesifikke spec-er.\n",
    "version": "0.1.0",
    "contact": {
      "name": "ketl support",
      "url": "https://ketl.no",
      "email": "support@ketl.no"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://data.ketl.no/vilkar"
    },
    "termsOfService": "https://data.ketl.no/vilkar",
    "x-status": "draft",
    "x-issues": [
      "https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/issues/408",
      "https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/issues/410",
      "https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/issues/412",
      "https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/issues/413"
    ]
  },
  "servers": [
    {
      "url": "https://data.ketl.no",
      "description": "Production (data.ketl.no — app-domenet, alle API-er under /api/** og /mcp/**)"
    },
    {
      "url": "https://ketl-innsikt.web.app",
      "description": "Firebase Hosting (canonical web origin)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local dev (`npm run dev`)"
    }
  ],
  "tags": [
    {
      "name": "data-proxy",
      "description": "Eksisterende proxy mot service. Hvert kall ruter videre til\n`/v1/connectors/{kilde}/{endepunkt}` på service-siden. Stabilt API.\n"
    },
    {
      "name": "companies",
      "description": "Normaliserte selskaps-entiteter (Tier 1 MVP per #410). Kilde-agnostisk\n— én `Company` uavhengig av om data kommer fra Brreg, CVR, YTJ eller\nBolagsverket.\n"
    },
    {
      "name": "sources",
      "description": "Kilde-katalog. Lister hver datakilde web kjenner til, med status\n(connected/planned/scraping) og lenker til dokumentasjon.\n"
    },
    {
      "name": "meta",
      "description": "Helse, versjon og diagnostikk. Brukes av admin-UI og uptime-bot.\n"
    }
  ],
  "paths": {
    "/api/data/{kilde}/{endepunkt}": {
      "parameters": [
        {
          "name": "kilde",
          "in": "path",
          "required": true,
          "description": "Kilde-nøkkel (kebab-case). Se `Source.key` for full liste.",
          "schema": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          }
        },
        {
          "name": "endepunkt",
          "in": "path",
          "required": true,
          "description": "Ressurs-path inn mot kilden. Format og lovlige verdier varierer per\nkilde — se [docs/DATA_PROXY.md](https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/blob/main/docs/DATA_PROXY.md).\n",
          "schema": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "selskap/974760673"
          }
        }
      ],
      "get": {
        "tags": [
          "data-proxy"
        ],
        "operationId": "proxyConnectorGet",
        "summary": "Proxy GET mot ketl-innsiktsmaskin-service",
        "description": "Pass-through GET-call til service. Path-segmentet `endepunkt` dekoder\ndirekte til `/v1/connectors/{kilde}/{endepunkt}` på service.\nAuth + rate-limit + billing skjer i Cloud Function-laget før kallet\nforwardes.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Pass-through fra service. Schema er kilde-spesifikk.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "401": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "403": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "404": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "429": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "503": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "data-proxy"
        ],
        "operationId": "proxyConnectorPost",
        "summary": "Proxy POST mot ketl-innsiktsmaskin-service",
        "description": "Brukes av kilder som krever større queries enn det som passer i URL\n(SSB PxWebApi v2 ≥ 2000 tegn, se docs/connectors/ssb-pxwebapi-spec.md).\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pass-through fra service. Schema er kilde-spesifikk.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "429": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "503": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/companies/{orgnr}": {
      "parameters": [
        {
          "name": "orgnr",
          "in": "path",
          "required": true,
          "description": "Norsk organisasjonsnummer (9 siffer, MOD-11-validert).",
          "schema": {
            "type": "string",
            "pattern": "^\\d{9}$",
            "example": "974760673"
          }
        }
      ],
      "get": {
        "tags": [
          "companies"
        ],
        "operationId": "getCompanyByOrgnr",
        "summary": "Hent ett selskap (normalisert)",
        "description": "Returnerer den siste normaliserte snapshoten av selskapet, kilde-agnostisk.\nBak kulissene leses canonical doc fra Firestore (`companies/{orgnr}`)\neller faller tilbake til live-fetch via Brreg-connector hvis cache mangler.\nStatus: **planned** — implementeres etter #189 Brreg pilot lander.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Selskap funnet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "Company",
                  "description": "Normalisert selskaps-entitet. For Tier 1 dekker den norske AS/ASA fra Brreg.\nTier 2 utvider med svenske/danske/finske selskaper via lands-spesifikke\n`nationalIdType`-verdier (#410).\n",
                  "required": [
                    "orgnr",
                    "navn",
                    "selskapsform",
                    "forretningsadresse",
                    "kontakt",
                    "sistHentetIso"
                  ],
                  "properties": {
                    "orgnr": {
                      "type": "string",
                      "pattern": "^\\d{9}$",
                      "description": "Norsk organisasjonsnummer (9 siffer).",
                      "example": "974760673"
                    },
                    "navn": {
                      "type": "string",
                      "minLength": 1,
                      "description": "Registrert selskapsnavn fra Brreg.",
                      "example": "Statens vegvesen"
                    },
                    "mvaNavn": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "MVA-registrert navn hvis forskjellig fra hoved-navnet."
                    },
                    "selskapsform": {
                      "type": "string",
                      "enum": [
                        "AS",
                        "ASA",
                        "ENK",
                        "ANS",
                        "SA",
                        "IKS",
                        "KF",
                        "STI",
                        "FLI",
                        "ANNET"
                      ],
                      "description": "Selskapsform per Brreg-kategorisering."
                    },
                    "antallAnsatte": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall ansatte fra siste Brreg-snapshot."
                    },
                    "stiftetIso": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Stiftelsesdato (ISO-8601 `YYYY-MM-DD`).",
                      "example": "1864-07-01"
                    },
                    "forretningsadresse": {
                      "type": "object",
                      "title": "CompanyAddress",
                      "required": [
                        "adresse",
                        "postnummer",
                        "poststed",
                        "kommunenummer",
                        "land"
                      ],
                      "properties": {
                        "adresse": {
                          "type": "array",
                          "description": "Adresselinjer (gateadresse, etasje, etc.).",
                          "items": {
                            "type": "string"
                          },
                          "minItems": 0,
                          "maxItems": 3
                        },
                        "postnummer": {
                          "type": "string",
                          "pattern": "^\\d{4}$",
                          "description": "Norsk postnummer (4 siffer)."
                        },
                        "poststed": {
                          "type": "string",
                          "minLength": 1
                        },
                        "kommunenummer": {
                          "type": "string",
                          "pattern": "^\\d{4}$",
                          "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
                        },
                        "land": {
                          "type": "string",
                          "enum": [
                            "NO"
                          ],
                          "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
                        }
                      },
                      "example": {
                        "adresse": [
                          "Akersveien 26"
                        ],
                        "postnummer": "0177",
                        "poststed": "Oslo",
                        "kommunenummer": "0301",
                        "land": "NO"
                      }
                    },
                    "postadresse": {
                      "oneOf": [
                        {
                          "type": "object",
                          "title": "CompanyAddress",
                          "required": [
                            "adresse",
                            "postnummer",
                            "poststed",
                            "kommunenummer",
                            "land"
                          ],
                          "properties": {
                            "adresse": {
                              "type": "array",
                              "description": "Adresselinjer (gateadresse, etasje, etc.).",
                              "items": {
                                "type": "string"
                              },
                              "minItems": 0,
                              "maxItems": 3
                            },
                            "postnummer": {
                              "type": "string",
                              "pattern": "^\\d{4}$",
                              "description": "Norsk postnummer (4 siffer)."
                            },
                            "poststed": {
                              "type": "string",
                              "minLength": 1
                            },
                            "kommunenummer": {
                              "type": "string",
                              "pattern": "^\\d{4}$",
                              "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
                            },
                            "land": {
                              "type": "string",
                              "enum": [
                                "NO"
                              ],
                              "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
                            }
                          },
                          "example": {
                            "adresse": [
                              "Akersveien 26"
                            ],
                            "postnummer": "0177",
                            "poststed": "Oslo",
                            "kommunenummer": "0301",
                            "land": "NO"
                          }
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "Postadresse hvis forskjellig fra forretningsadresse."
                    },
                    "kontakt": {
                      "type": "object",
                      "description": "Kontaktinformasjon (alle felter kan være null).",
                      "required": [
                        "epost",
                        "telefon",
                        "hjemmeside"
                      ],
                      "properties": {
                        "epost": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "email"
                        },
                        "telefon": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "hjemmeside": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uri"
                        }
                      }
                    },
                    "naeringskode": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Primært NACE-nummer (f.eks. `84.130`).",
                      "example": "84.130"
                    },
                    "sistHentetIso": {
                      "type": "string",
                      "minLength": 20,
                      "description": "Hentet-tidspunkt fra Brreg, ISO-8601.",
                      "example": "2026-05-04T08:30:00Z"
                    }
                  },
                  "example": {
                    "orgnr": "974760673",
                    "navn": "Statens vegvesen",
                    "mvaNavn": null,
                    "selskapsform": "ANNET",
                    "antallAnsatte": 7500,
                    "stiftetIso": "1864-07-01",
                    "forretningsadresse": {
                      "adresse": [
                        "Postboks 1010"
                      ],
                      "postnummer": "2605",
                      "poststed": "Lillehammer",
                      "kommunenummer": "3403",
                      "land": "NO"
                    },
                    "postadresse": null,
                    "kontakt": {
                      "epost": "firmapost@vegvesen.no",
                      "telefon": "+47 22 07 30 00",
                      "hjemmeside": "https://www.vegvesen.no"
                    },
                    "naeringskode": "84.130",
                    "sistHentetIso": "2026-05-04T08:30:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "404": {
            "description": "Selskap finnes ikke (NXDOMAIN i Brreg eller ikke aktivert i ketl).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "429": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/companies/{orgnr}/events": {
      "parameters": [
        {
          "name": "orgnr",
          "in": "path",
          "required": true,
          "description": "Norsk organisasjonsnummer (9 siffer, MOD-11-validert).",
          "schema": {
            "type": "string",
            "pattern": "^\\d{9}$",
            "example": "974760673"
          }
        }
      ],
      "get": {
        "tags": [
          "companies"
        ],
        "operationId": "listCompanyEvents",
        "summary": "List events for ett selskap",
        "description": "Returnerer en paginert event-stream for det gitte organisasjonsnummeret,\nsortert med nyeste først. Events skrives av Brreg endringsfeed\n(`functions/src/brreg-feed.ts`) hver 15. minutt.\n\n**Cursor-paginering:** Send `next_cursor`-verdien fra forrige respons\nsom `?cursor=...` for å hente neste side. Cursor er opak (base64url-\nencoded interne posisjons-info) — ikke forsøk å parse innholdet.\n\n**`X-Ketl-Serving-Mode`-header** settes alltid til `warehouse` siden\ndenne resursen kun finnes i Firestore (ingen upstream-fallback).\n\nStatus: **implementert** for `brreg`-kilde (moat #72 Tier 1).\nAndre kilder (Finanstilsynet, Kartverket, etc.) legges til etter hvert\nsom deres endringsfeeds skriver til `company_events`.\n",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "description": "Antall items per side. Default 50, cap 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "description": "Opaque cursor fra `next_cursor` i forrige respons.",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginert event-liste (kan være tom).",
            "headers": {
              "X-Ketl-Serving-Mode": {
                "description": "Alltid `warehouse` for events-resursen.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "warehouse"
                  ]
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "PaginatedCompanyEvents",
                  "required": [
                    "items",
                    "next_cursor",
                    "limit",
                    "orgnr"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "title": "CompanyEventItem",
                        "required": [
                          "eventId",
                          "orgnr",
                          "eventType",
                          "source",
                          "detectedIso"
                        ],
                        "properties": {
                          "eventId": {
                            "type": "string",
                            "description": "Stabil event-id på formen `{source}:{externalCursor}`. For brreg-feed:\n`brreg_enhetsregister:{oppdateringsid}`.\n",
                            "pattern": "^[a-z][a-z0-9_]*:[A-Za-z0-9_.\\-]+$",
                            "example": "brreg_enhetsregister:12345678"
                          },
                          "orgnr": {
                            "type": "string",
                            "pattern": "^\\d{9}$",
                            "example": "974760673"
                          },
                          "eventType": {
                            "type": "string",
                            "description": "Canonical hendelsestype. For Brreg endringsfeed mappes\n`endringstype` til `brreg:enhet_opprettet|endret|slettet|fjernet`.\nUkjente verdier mappes til `brreg:enhet_ukjent` (forward-kompatibel).\n",
                            "pattern": "^[a-z][a-z0-9_-]*:[a-z][a-z0-9_-]*$",
                            "example": "brreg:enhet_endret"
                          },
                          "source": {
                            "type": "string",
                            "title": "SourceKey",
                            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
                            "enum": [
                              "brreg",
                              "ssb",
                              "norges-bank",
                              "enhetsregister",
                              "proff",
                              "bygg-anlegg",
                              "eksportor",
                              "kartverket",
                              "sodir",
                              "ais",
                              "artskart",
                              "avinor",
                              "barentswatch",
                              "elhub",
                              "fhi",
                              "finanstilsynet",
                              "fisheries",
                              "fri-nettleie",
                              "frost",
                              "geonorge",
                              "hoyesterett",
                              "konkurs",
                              "kommunedatabasen",
                              "kostra",
                              "lakselus",
                              "lovtidend",
                              "luftkvalitet",
                              "lysingsblad",
                              "mattilsynet-smilefjes",
                              "met",
                              "nav",
                              "nb-dhlab",
                              "norske-utslipp",
                              "nve",
                              "nvdb",
                              "oslobors",
                              "patentstyret",
                              "politiloggen",
                              "regjeringen",
                              "rss-news",
                              "sdir",
                              "stortinget",
                              "stromprise",
                              "udir",
                              "connectors",
                              "data-quality",
                              "audit",
                              "companies",
                              "persons",
                              "kg",
                              "sync",
                              "config",
                              "sync-state",
                              "storage",
                              "discovery",
                              "lineage",
                              "backup",
                              "metrics",
                              "dashboard",
                              "brreg-endringer",
                              "nav-grunnbelop"
                            ]
                          },
                          "detectedIso": {
                            "type": "string",
                            "minLength": 20,
                            "description": "ISO-8601 tidsstempel da event ble observert/skrevet til warehouse.",
                            "example": "2026-05-14T08:30:00.000Z"
                          },
                          "rawEndringstype": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Råverdi fra kilde-API. For Brreg: `\"Ny\" | \"Endring\" | \"Sletting\" | \"Fjernet\"`.\n`null` hvis kilden ikke leverer en endringstype.\n",
                            "example": "Endring"
                          },
                          "externalCursor": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Kilde-spesifikk monotonisk cursor. For Brreg: `oppdateringsid` som string.\nBrukes internt til pagination — ikke en del av offentlig API-kontrakt.\n",
                            "example": "12345678"
                          }
                        }
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Opaque base64url-encoded cursor for neste side. `null` når listen er\nferdig. Klient skal sende cursoren videre uten å forsøke å parse den.\n",
                      "example": "eyJvIjoxMjM0NTY3Nn0"
                    },
                    "limit": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 100,
                      "description": "Antall items per side. Default 50, cap 100. Echoes inn-parameter slik\nat klient kan verifisere at serveren respekterte verdien.\n",
                      "example": 50
                    },
                    "orgnr": {
                      "type": "string",
                      "pattern": "^\\d{9}$",
                      "description": "Echoes path-parameteren slik at responsen er self-contained.",
                      "example": "974760673"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Ugyldig orgnr, limit eller cursor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "401": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "429": {
            "description": "Standardisert feil per RFC 9457.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          },
          "502": {
            "description": "Firestore-leseringsfeil (typisk manglende composite-index).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/_meta/sources": {
      "get": {
        "tags": [
          "sources",
          "meta"
        ],
        "operationId": "listSources",
        "summary": "List alle kilder ketl kjenner til",
        "description": "Returnerer alle kilder med status og dokumentasjons-lenker. Filtreres\nvalgfritt på `country` og `status`.\n",
        "parameters": [
          {
            "in": "query",
            "name": "country",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "NO",
                "SE",
                "DK",
                "FI",
                "IS",
                "EU"
              ]
            }
          },
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "connected",
                "planned",
                "scraping",
                "deprecated"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Kilde-liste.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items",
                    "total"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "title": "Source",
                        "required": [
                          "key",
                          "name",
                          "status",
                          "country"
                        ],
                        "properties": {
                          "key": {
                            "type": "string",
                            "title": "SourceKey",
                            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
                            "enum": [
                              "brreg",
                              "ssb",
                              "norges-bank",
                              "enhetsregister",
                              "proff",
                              "bygg-anlegg",
                              "eksportor",
                              "kartverket",
                              "sodir",
                              "ais",
                              "artskart",
                              "avinor",
                              "barentswatch",
                              "elhub",
                              "fhi",
                              "finanstilsynet",
                              "fisheries",
                              "fri-nettleie",
                              "frost",
                              "geonorge",
                              "hoyesterett",
                              "konkurs",
                              "kommunedatabasen",
                              "kostra",
                              "lakselus",
                              "lovtidend",
                              "luftkvalitet",
                              "lysingsblad",
                              "mattilsynet-smilefjes",
                              "met",
                              "nav",
                              "nb-dhlab",
                              "norske-utslipp",
                              "nve",
                              "nvdb",
                              "oslobors",
                              "patentstyret",
                              "politiloggen",
                              "regjeringen",
                              "rss-news",
                              "sdir",
                              "stortinget",
                              "stromprise",
                              "udir",
                              "connectors",
                              "data-quality",
                              "audit",
                              "companies",
                              "persons",
                              "kg",
                              "sync",
                              "config",
                              "sync-state",
                              "storage",
                              "discovery",
                              "lineage",
                              "backup",
                              "metrics",
                              "dashboard",
                              "brreg-endringer",
                              "nav-grunnbelop"
                            ]
                          },
                          "name": {
                            "type": "string",
                            "description": "Menneskelig navn på kilden.",
                            "example": "Brønnøysundregistrene Enhetsregisteret"
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "maxLength": 500
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "connected",
                              "planned",
                              "scraping",
                              "deprecated"
                            ],
                            "description": "`connected`  = produserer ekte data via /api/data eller /api/v1\n`planned`    = listet i konfig, men ikke koblet til\n`scraping`   = ingen åpent API; data kommer fra scraper-flåten (#214)\n`deprecated` = under avvikling\n"
                          },
                          "country": {
                            "type": "string",
                            "enum": [
                              "NO",
                              "SE",
                              "DK",
                              "FI",
                              "IS",
                              "EU"
                            ],
                            "description": "Hvilket land/region kilden dekker."
                          },
                          "segments": {
                            "type": "array",
                            "description": "ICP-segmenter denne kilden er primært relevant for (#98).",
                            "items": {
                              "type": "string",
                              "enum": [
                                "revisor",
                                "konsulent",
                                "journalist",
                                "kommune",
                                "compliance",
                                "aktuar",
                                "eiendom",
                                "utvikler",
                                "academia",
                                "pe-vc",
                                "csrd",
                                "ai-act",
                                "eksportor",
                                "casp",
                                "bygg-anlegg"
                              ]
                            }
                          },
                          "docsUrl": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "uri",
                            "description": "Lenke til kildens egen API-dokumentasjon (data.brreg.no, ws.geonorge.no, ...)."
                          }
                        },
                        "example": {
                          "key": "brreg",
                          "name": "Brønnøysundregistrene Enhetsregisteret",
                          "description": "Norsk selskapsregister med ~1M aktive juridiske enheter.",
                          "status": "connected",
                          "country": "NO",
                          "segments": [
                            "revisor",
                            "compliance",
                            "journalist"
                          ],
                          "docsUrl": "https://data.brreg.no/enhetsregisteret/api/dokumentasjon/no/index.html"
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Totalt antall (etter ev. filter)."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/_meta/sources/{key}": {
      "parameters": [
        {
          "in": "path",
          "name": "key",
          "required": true,
          "description": "Kilde-nøkkel (kebab-case). Se `Source.key` for full liste.",
          "schema": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          }
        }
      ],
      "get": {
        "tags": [
          "sources",
          "meta"
        ],
        "operationId": "getSourceByKey",
        "summary": "Hent én kilde-detalj",
        "description": "Returnerer detaljer om én kilde, inkludert MCP-verktøy, API-endepunkter\n(på kilden, ikke på vårt API), oppdateringsfrekvens og lisens.\n",
        "responses": {
          "200": {
            "description": "Kilde funnet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "Source",
                  "required": [
                    "key",
                    "name",
                    "status",
                    "country"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "title": "SourceKey",
                      "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
                      "enum": [
                        "brreg",
                        "ssb",
                        "norges-bank",
                        "enhetsregister",
                        "proff",
                        "bygg-anlegg",
                        "eksportor",
                        "kartverket",
                        "sodir",
                        "ais",
                        "artskart",
                        "avinor",
                        "barentswatch",
                        "elhub",
                        "fhi",
                        "finanstilsynet",
                        "fisheries",
                        "fri-nettleie",
                        "frost",
                        "geonorge",
                        "hoyesterett",
                        "konkurs",
                        "kommunedatabasen",
                        "kostra",
                        "lakselus",
                        "lovtidend",
                        "luftkvalitet",
                        "lysingsblad",
                        "mattilsynet-smilefjes",
                        "met",
                        "nav",
                        "nb-dhlab",
                        "norske-utslipp",
                        "nve",
                        "nvdb",
                        "oslobors",
                        "patentstyret",
                        "politiloggen",
                        "regjeringen",
                        "rss-news",
                        "sdir",
                        "stortinget",
                        "stromprise",
                        "udir",
                        "connectors",
                        "data-quality",
                        "audit",
                        "companies",
                        "persons",
                        "kg",
                        "sync",
                        "config",
                        "sync-state",
                        "storage",
                        "discovery",
                        "lineage",
                        "backup",
                        "metrics",
                        "dashboard",
                        "brreg-endringer",
                        "nav-grunnbelop"
                      ]
                    },
                    "name": {
                      "type": "string",
                      "description": "Menneskelig navn på kilden.",
                      "example": "Brønnøysundregistrene Enhetsregisteret"
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "maxLength": 500
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "connected",
                        "planned",
                        "scraping",
                        "deprecated"
                      ],
                      "description": "`connected`  = produserer ekte data via /api/data eller /api/v1\n`planned`    = listet i konfig, men ikke koblet til\n`scraping`   = ingen åpent API; data kommer fra scraper-flåten (#214)\n`deprecated` = under avvikling\n"
                    },
                    "country": {
                      "type": "string",
                      "enum": [
                        "NO",
                        "SE",
                        "DK",
                        "FI",
                        "IS",
                        "EU"
                      ],
                      "description": "Hvilket land/region kilden dekker."
                    },
                    "segments": {
                      "type": "array",
                      "description": "ICP-segmenter denne kilden er primært relevant for (#98).",
                      "items": {
                        "type": "string",
                        "enum": [
                          "revisor",
                          "konsulent",
                          "journalist",
                          "kommune",
                          "compliance",
                          "aktuar",
                          "eiendom",
                          "utvikler",
                          "academia",
                          "pe-vc",
                          "csrd",
                          "ai-act",
                          "eksportor",
                          "casp",
                          "bygg-anlegg"
                        ]
                      }
                    },
                    "docsUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri",
                      "description": "Lenke til kildens egen API-dokumentasjon (data.brreg.no, ws.geonorge.no, ...)."
                    }
                  },
                  "example": {
                    "key": "brreg",
                    "name": "Brønnøysundregistrene Enhetsregisteret",
                    "description": "Norsk selskapsregister med ~1M aktive juridiske enheter.",
                    "status": "connected",
                    "country": "NO",
                    "segments": [
                      "revisor",
                      "compliance",
                      "journalist"
                    ],
                    "docsUrl": "https://data.brreg.no/enhetsregisteret/api/dokumentasjon/no/index.html"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Kilden er ikke kjent.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "required": [
                    "type",
                    "title",
                    "status"
                  ],
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri-reference",
                      "default": "about:blank",
                      "description": "Stabil identifikator for feiltypen.",
                      "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                    },
                    "title": {
                      "type": "string",
                      "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                    },
                    "status": {
                      "type": "integer",
                      "minimum": 100,
                      "maximum": 599
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Konkret beskrivelse av denne instansen."
                    },
                    "instance": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri-reference",
                      "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                    },
                    "ketlRequestId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                    },
                    "ketlRetryAfterMs": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "minimum": 0,
                      "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                    }
                  },
                  "example": {
                    "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                    "title": "Rate limit exceeded",
                    "status": 429,
                    "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                    "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                    "ketlRetryAfterMs": 60000
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/_meta/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "getHealth",
        "summary": "Health-check",
        "description": "Returnerer status for ketl + alle kritiske upstream-avhengigheter.\nBrukes av admin-dashboard og eksterne uptime-bots.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "ok eller degraded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "Health",
                  "required": [
                    "status",
                    "version",
                    "timestamp"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "degraded",
                        "down"
                      ]
                    },
                    "version": {
                      "type": "string",
                      "description": "Build-SHA + tidstempel (`<sha>-<iso>`).",
                      "example": "c36f542-2026-05-04T12:00:00Z"
                    },
                    "timestamp": {
                      "type": "string",
                      "minLength": 20,
                      "description": "ISO-8601 server-tid."
                    },
                    "upstream": {
                      "type": "object",
                      "description": "Status for hver upstream-avhengighet.",
                      "additionalProperties": {
                        "type": "string",
                        "enum": [
                          "ok",
                          "degraded",
                          "down",
                          "unknown"
                        ]
                      },
                      "example": {
                        "service": "ok",
                        "firestore": "ok",
                        "stripe": "ok"
                      }
                    },
                    "diagnostics": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "ketl-spesifikke diagnostiske felt (utenfor offentlig RFC-kontrakt).\nBrukes av admin-UI og uptime-bots for å se proxy-tilstand. Kan\ninneholde proxyBaseUrl, proxyKnownDead, proxyKnownDeadIssue (#414).\n",
                      "additionalProperties": true,
                      "example": {
                        "proxyBaseUrl": "https://data.ketl.no",
                        "proxyBaseUrlSource": "prod-default",
                        "proxyKnownDead": false,
                        "proxyKnownDeadIssue": null
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "down (alle upstream nede).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "Health",
                  "required": [
                    "status",
                    "version",
                    "timestamp"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "degraded",
                        "down"
                      ]
                    },
                    "version": {
                      "type": "string",
                      "description": "Build-SHA + tidstempel (`<sha>-<iso>`).",
                      "example": "c36f542-2026-05-04T12:00:00Z"
                    },
                    "timestamp": {
                      "type": "string",
                      "minLength": 20,
                      "description": "ISO-8601 server-tid."
                    },
                    "upstream": {
                      "type": "object",
                      "description": "Status for hver upstream-avhengighet.",
                      "additionalProperties": {
                        "type": "string",
                        "enum": [
                          "ok",
                          "degraded",
                          "down",
                          "unknown"
                        ]
                      },
                      "example": {
                        "service": "ok",
                        "firestore": "ok",
                        "stripe": "ok"
                      }
                    },
                    "diagnostics": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "ketl-spesifikke diagnostiske felt (utenfor offentlig RFC-kontrakt).\nBrukes av admin-UI og uptime-bots for å se proxy-tilstand. Kan\ninneholde proxyBaseUrl, proxyKnownDead, proxyKnownDeadIssue (#414).\n",
                      "additionalProperties": true,
                      "example": {
                        "proxyBaseUrl": "https://data.ketl.no",
                        "proxyBaseUrlSource": "prod-default",
                        "proxyKnownDead": false,
                        "proxyKnownDeadIssue": null
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/_meta/openapi.json": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "getOpenApiSpec",
        "summary": "Hent OpenAPI 3.1-spec (bundled JSON)",
        "description": "Returnerer den bundlede OpenAPI 3.1-spec for ketl Unified API.\nBrukes av Swagger UI, ReDoc, openapi-explorer-extensions og lignende\nklienter som vil generere SDK-er fra en stabil URL.\n\nInnholdet er identisk med `openapi/bundled.json` på `main`-branchen\n+ alle `$ref`-fragmenter inlinet via `scripts/bundle-openapi.mjs`.\n\nCache-Control: `public, max-age=3600` — spec endres sjelden.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 dokument (JSON).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "OpenAPI 3.1.0 dokument.",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Company": {
        "type": "object",
        "title": "Company",
        "description": "Normalisert selskaps-entitet. For Tier 1 dekker den norske AS/ASA fra Brreg.\nTier 2 utvider med svenske/danske/finske selskaper via lands-spesifikke\n`nationalIdType`-verdier (#410).\n",
        "required": [
          "orgnr",
          "navn",
          "selskapsform",
          "forretningsadresse",
          "kontakt",
          "sistHentetIso"
        ],
        "properties": {
          "orgnr": {
            "type": "string",
            "pattern": "^\\d{9}$",
            "description": "Norsk organisasjonsnummer (9 siffer).",
            "example": "974760673"
          },
          "navn": {
            "type": "string",
            "minLength": 1,
            "description": "Registrert selskapsnavn fra Brreg.",
            "example": "Statens vegvesen"
          },
          "mvaNavn": {
            "type": [
              "string",
              "null"
            ],
            "description": "MVA-registrert navn hvis forskjellig fra hoved-navnet."
          },
          "selskapsform": {
            "type": "string",
            "enum": [
              "AS",
              "ASA",
              "ENK",
              "ANS",
              "SA",
              "IKS",
              "KF",
              "STI",
              "FLI",
              "ANNET"
            ],
            "description": "Selskapsform per Brreg-kategorisering."
          },
          "antallAnsatte": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Antall ansatte fra siste Brreg-snapshot."
          },
          "stiftetIso": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stiftelsesdato (ISO-8601 `YYYY-MM-DD`).",
            "example": "1864-07-01"
          },
          "forretningsadresse": {
            "type": "object",
            "title": "CompanyAddress",
            "required": [
              "adresse",
              "postnummer",
              "poststed",
              "kommunenummer",
              "land"
            ],
            "properties": {
              "adresse": {
                "type": "array",
                "description": "Adresselinjer (gateadresse, etasje, etc.).",
                "items": {
                  "type": "string"
                },
                "minItems": 0,
                "maxItems": 3
              },
              "postnummer": {
                "type": "string",
                "pattern": "^\\d{4}$",
                "description": "Norsk postnummer (4 siffer)."
              },
              "poststed": {
                "type": "string",
                "minLength": 1
              },
              "kommunenummer": {
                "type": "string",
                "pattern": "^\\d{4}$",
                "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
              },
              "land": {
                "type": "string",
                "enum": [
                  "NO"
                ],
                "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
              }
            },
            "example": {
              "adresse": [
                "Akersveien 26"
              ],
              "postnummer": "0177",
              "poststed": "Oslo",
              "kommunenummer": "0301",
              "land": "NO"
            }
          },
          "postadresse": {
            "oneOf": [
              {
                "type": "object",
                "title": "CompanyAddress",
                "required": [
                  "adresse",
                  "postnummer",
                  "poststed",
                  "kommunenummer",
                  "land"
                ],
                "properties": {
                  "adresse": {
                    "type": "array",
                    "description": "Adresselinjer (gateadresse, etasje, etc.).",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 0,
                    "maxItems": 3
                  },
                  "postnummer": {
                    "type": "string",
                    "pattern": "^\\d{4}$",
                    "description": "Norsk postnummer (4 siffer)."
                  },
                  "poststed": {
                    "type": "string",
                    "minLength": 1
                  },
                  "kommunenummer": {
                    "type": "string",
                    "pattern": "^\\d{4}$",
                    "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
                  },
                  "land": {
                    "type": "string",
                    "enum": [
                      "NO"
                    ],
                    "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
                  }
                },
                "example": {
                  "adresse": [
                    "Akersveien 26"
                  ],
                  "postnummer": "0177",
                  "poststed": "Oslo",
                  "kommunenummer": "0301",
                  "land": "NO"
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Postadresse hvis forskjellig fra forretningsadresse."
          },
          "kontakt": {
            "type": "object",
            "description": "Kontaktinformasjon (alle felter kan være null).",
            "required": [
              "epost",
              "telefon",
              "hjemmeside"
            ],
            "properties": {
              "epost": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "email"
              },
              "telefon": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "hjemmeside": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              }
            }
          },
          "naeringskode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Primært NACE-nummer (f.eks. `84.130`).",
            "example": "84.130"
          },
          "sistHentetIso": {
            "type": "string",
            "minLength": 20,
            "description": "Hentet-tidspunkt fra Brreg, ISO-8601.",
            "example": "2026-05-04T08:30:00Z"
          }
        },
        "example": {
          "orgnr": "974760673",
          "navn": "Statens vegvesen",
          "mvaNavn": null,
          "selskapsform": "ANNET",
          "antallAnsatte": 7500,
          "stiftetIso": "1864-07-01",
          "forretningsadresse": {
            "adresse": [
              "Postboks 1010"
            ],
            "postnummer": "2605",
            "poststed": "Lillehammer",
            "kommunenummer": "3403",
            "land": "NO"
          },
          "postadresse": null,
          "kontakt": {
            "epost": "firmapost@vegvesen.no",
            "telefon": "+47 22 07 30 00",
            "hjemmeside": "https://www.vegvesen.no"
          },
          "naeringskode": "84.130",
          "sistHentetIso": "2026-05-04T08:30:00Z"
        }
      },
      "CompanyAddress": {
        "type": "object",
        "title": "CompanyAddress",
        "required": [
          "adresse",
          "postnummer",
          "poststed",
          "kommunenummer",
          "land"
        ],
        "properties": {
          "adresse": {
            "type": "array",
            "description": "Adresselinjer (gateadresse, etasje, etc.).",
            "items": {
              "type": "string"
            },
            "minItems": 0,
            "maxItems": 3
          },
          "postnummer": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Norsk postnummer (4 siffer)."
          },
          "poststed": {
            "type": "string",
            "minLength": 1
          },
          "kommunenummer": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
          },
          "land": {
            "type": "string",
            "enum": [
              "NO"
            ],
            "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
          }
        },
        "example": {
          "adresse": [
            "Akersveien 26"
          ],
          "postnummer": "0177",
          "poststed": "Oslo",
          "kommunenummer": "0301",
          "land": "NO"
        }
      },
      "Snapshot": {
        "type": "object",
        "title": "Snapshot",
        "required": [
          "snapshotId",
          "entityType",
          "entityId",
          "fetchedIso",
          "source",
          "payload"
        ],
        "properties": {
          "snapshotId": {
            "type": "string",
            "description": "ULID, tidssortert. Stabil og deterministisk.",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "example": "01HZN3R5QKV8MQX7K9C2W6J5RT"
          },
          "entityType": {
            "type": "string",
            "description": "Domene-entitet snapshoten beskriver.",
            "enum": [
              "company",
              "person",
              "address",
              "regulation",
              "case",
              "statistics"
            ]
          },
          "entityId": {
            "type": "string",
            "description": "Domene-id (orgnr for company, matrikkel for address, m.fl.)."
          },
          "fetchedIso": {
            "type": "string",
            "minLength": 20,
            "description": "Når connectoren hentet payload-en. ISO-8601."
          },
          "source": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          },
          "apiVersion": {
            "type": [
              "string",
              "null"
            ],
            "description": "Versjon av kilde-API observert ved fetch (drift-deteksjon)."
          },
          "payload": {
            "type": "object",
            "description": "Kanonisk JSON-representasjon av kilde-responsen, post-normalisering.\nBegrenset til ~1 MB; chunkes ved overskridelse.\n",
            "additionalProperties": true
          },
          "payloadHash": {
            "type": "string",
            "description": "SHA-256 av canonicalized payload. Brukes for diff-deteksjon.",
            "pattern": "^[0-9a-f]{64}$"
          },
          "changeReason": {
            "type": "string",
            "enum": [
              "initial",
              "value-change",
              "periodic"
            ],
            "description": "Hvorfor snapshoten ble skrevet."
          }
        }
      },
      "Event": {
        "type": "object",
        "title": "Event",
        "required": [
          "eventId",
          "entityType",
          "entityId",
          "eventType",
          "detectedIso",
          "source"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "description": "ULID, tidssortert. Stabil mellom replays.",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$"
          },
          "entityType": {
            "type": "string",
            "enum": [
              "company",
              "person",
              "address",
              "regulation",
              "case"
            ]
          },
          "entityId": {
            "type": "string"
          },
          "eventType": {
            "type": "string",
            "description": "Domene-spesifikk hendelsestype. Eksempler: `brreg:name_changed`,\n`brreg:role_added`, `finanstilsynet:licence_revoked`, `kartverket:owner_changed`.\n",
            "pattern": "^[a-z][a-z0-9_-]*:[a-z][a-z0-9_-]*$",
            "example": "brreg:name_changed"
          },
          "detectedIso": {
            "type": "string",
            "minLength": 20
          },
          "source": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          },
          "before": {
            "type": [
              "object",
              "null"
            ],
            "description": "Felt-subset fra forrige snapshot.",
            "additionalProperties": true
          },
          "after": {
            "type": [
              "object",
              "null"
            ],
            "description": "Felt-subset fra nåværende snapshot.",
            "additionalProperties": true
          },
          "snapshotIdBefore": {
            "type": [
              "string",
              "null"
            ]
          },
          "snapshotIdAfter": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Source": {
        "type": "object",
        "title": "Source",
        "required": [
          "key",
          "name",
          "status",
          "country"
        ],
        "properties": {
          "key": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          },
          "name": {
            "type": "string",
            "description": "Menneskelig navn på kilden.",
            "example": "Brønnøysundregistrene Enhetsregisteret"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "planned",
              "scraping",
              "deprecated"
            ],
            "description": "`connected`  = produserer ekte data via /api/data eller /api/v1\n`planned`    = listet i konfig, men ikke koblet til\n`scraping`   = ingen åpent API; data kommer fra scraper-flåten (#214)\n`deprecated` = under avvikling\n"
          },
          "country": {
            "type": "string",
            "enum": [
              "NO",
              "SE",
              "DK",
              "FI",
              "IS",
              "EU"
            ],
            "description": "Hvilket land/region kilden dekker."
          },
          "segments": {
            "type": "array",
            "description": "ICP-segmenter denne kilden er primært relevant for (#98).",
            "items": {
              "type": "string",
              "enum": [
                "revisor",
                "konsulent",
                "journalist",
                "kommune",
                "compliance",
                "aktuar",
                "eiendom",
                "utvikler",
                "academia",
                "pe-vc",
                "csrd",
                "ai-act",
                "eksportor",
                "casp",
                "bygg-anlegg"
              ]
            }
          },
          "docsUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Lenke til kildens egen API-dokumentasjon (data.brreg.no, ws.geonorge.no, ...)."
          }
        },
        "example": {
          "key": "brreg",
          "name": "Brønnøysundregistrene Enhetsregisteret",
          "description": "Norsk selskapsregister med ~1M aktive juridiske enheter.",
          "status": "connected",
          "country": "NO",
          "segments": [
            "revisor",
            "compliance",
            "journalist"
          ],
          "docsUrl": "https://data.brreg.no/enhetsregisteret/api/dokumentasjon/no/index.html"
        }
      },
      "SourceKey": {
        "type": "string",
        "title": "SourceKey",
        "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
        "enum": [
          "brreg",
          "ssb",
          "norges-bank",
          "enhetsregister",
          "proff",
          "bygg-anlegg",
          "eksportor",
          "kartverket",
          "sodir",
          "ais",
          "artskart",
          "avinor",
          "barentswatch",
          "elhub",
          "fhi",
          "finanstilsynet",
          "fisheries",
          "fri-nettleie",
          "frost",
          "geonorge",
          "hoyesterett",
          "konkurs",
          "kommunedatabasen",
          "kostra",
          "lakselus",
          "lovtidend",
          "luftkvalitet",
          "lysingsblad",
          "mattilsynet-smilefjes",
          "met",
          "nav",
          "nb-dhlab",
          "norske-utslipp",
          "nve",
          "nvdb",
          "oslobors",
          "patentstyret",
          "politiloggen",
          "regjeringen",
          "rss-news",
          "sdir",
          "stortinget",
          "stromprise",
          "udir",
          "connectors",
          "data-quality",
          "audit",
          "companies",
          "persons",
          "kg",
          "sync",
          "config",
          "sync-state",
          "storage",
          "discovery",
          "lineage",
          "backup",
          "metrics",
          "dashboard",
          "brreg-endringer",
          "nav-grunnbelop"
        ]
      },
      "PaginatedCompanies": {
        "type": "object",
        "title": "PaginatedCompanies",
        "required": [
          "items",
          "next_cursor",
          "limit",
          "fields"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "title": "Company",
              "description": "Normalisert selskaps-entitet. For Tier 1 dekker den norske AS/ASA fra Brreg.\nTier 2 utvider med svenske/danske/finske selskaper via lands-spesifikke\n`nationalIdType`-verdier (#410).\n",
              "required": [
                "orgnr",
                "navn",
                "selskapsform",
                "forretningsadresse",
                "kontakt",
                "sistHentetIso"
              ],
              "properties": {
                "orgnr": {
                  "type": "string",
                  "pattern": "^\\d{9}$",
                  "description": "Norsk organisasjonsnummer (9 siffer).",
                  "example": "974760673"
                },
                "navn": {
                  "type": "string",
                  "minLength": 1,
                  "description": "Registrert selskapsnavn fra Brreg.",
                  "example": "Statens vegvesen"
                },
                "mvaNavn": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "MVA-registrert navn hvis forskjellig fra hoved-navnet."
                },
                "selskapsform": {
                  "type": "string",
                  "enum": [
                    "AS",
                    "ASA",
                    "ENK",
                    "ANS",
                    "SA",
                    "IKS",
                    "KF",
                    "STI",
                    "FLI",
                    "ANNET"
                  ],
                  "description": "Selskapsform per Brreg-kategorisering."
                },
                "antallAnsatte": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "description": "Antall ansatte fra siste Brreg-snapshot."
                },
                "stiftetIso": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Stiftelsesdato (ISO-8601 `YYYY-MM-DD`).",
                  "example": "1864-07-01"
                },
                "forretningsadresse": {
                  "type": "object",
                  "title": "CompanyAddress",
                  "required": [
                    "adresse",
                    "postnummer",
                    "poststed",
                    "kommunenummer",
                    "land"
                  ],
                  "properties": {
                    "adresse": {
                      "type": "array",
                      "description": "Adresselinjer (gateadresse, etasje, etc.).",
                      "items": {
                        "type": "string"
                      },
                      "minItems": 0,
                      "maxItems": 3
                    },
                    "postnummer": {
                      "type": "string",
                      "pattern": "^\\d{4}$",
                      "description": "Norsk postnummer (4 siffer)."
                    },
                    "poststed": {
                      "type": "string",
                      "minLength": 1
                    },
                    "kommunenummer": {
                      "type": "string",
                      "pattern": "^\\d{4}$",
                      "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
                    },
                    "land": {
                      "type": "string",
                      "enum": [
                        "NO"
                      ],
                      "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
                    }
                  },
                  "example": {
                    "adresse": [
                      "Akersveien 26"
                    ],
                    "postnummer": "0177",
                    "poststed": "Oslo",
                    "kommunenummer": "0301",
                    "land": "NO"
                  }
                },
                "postadresse": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "CompanyAddress",
                      "required": [
                        "adresse",
                        "postnummer",
                        "poststed",
                        "kommunenummer",
                        "land"
                      ],
                      "properties": {
                        "adresse": {
                          "type": "array",
                          "description": "Adresselinjer (gateadresse, etasje, etc.).",
                          "items": {
                            "type": "string"
                          },
                          "minItems": 0,
                          "maxItems": 3
                        },
                        "postnummer": {
                          "type": "string",
                          "pattern": "^\\d{4}$",
                          "description": "Norsk postnummer (4 siffer)."
                        },
                        "poststed": {
                          "type": "string",
                          "minLength": 1
                        },
                        "kommunenummer": {
                          "type": "string",
                          "pattern": "^\\d{4}$",
                          "description": "Norsk kommunenummer per Kartverkets matrikkel (4 siffer)."
                        },
                        "land": {
                          "type": "string",
                          "enum": [
                            "NO"
                          ],
                          "description": "Tier 1 dekker kun NO. Utvides til SE/DK/FI/IS i Tier 2 (#410)."
                        }
                      },
                      "example": {
                        "adresse": [
                          "Akersveien 26"
                        ],
                        "postnummer": "0177",
                        "poststed": "Oslo",
                        "kommunenummer": "0301",
                        "land": "NO"
                      }
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Postadresse hvis forskjellig fra forretningsadresse."
                },
                "kontakt": {
                  "type": "object",
                  "description": "Kontaktinformasjon (alle felter kan være null).",
                  "required": [
                    "epost",
                    "telefon",
                    "hjemmeside"
                  ],
                  "properties": {
                    "epost": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "email"
                    },
                    "telefon": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "hjemmeside": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    }
                  }
                },
                "naeringskode": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Primært NACE-nummer (f.eks. `84.130`).",
                  "example": "84.130"
                },
                "sistHentetIso": {
                  "type": "string",
                  "minLength": 20,
                  "description": "Hentet-tidspunkt fra Brreg, ISO-8601.",
                  "example": "2026-05-04T08:30:00Z"
                }
              },
              "example": {
                "orgnr": "974760673",
                "navn": "Statens vegvesen",
                "mvaNavn": null,
                "selskapsform": "ANNET",
                "antallAnsatte": 7500,
                "stiftetIso": "1864-07-01",
                "forretningsadresse": {
                  "adresse": [
                    "Postboks 1010"
                  ],
                  "postnummer": "2605",
                  "poststed": "Lillehammer",
                  "kommunenummer": "3403",
                  "land": "NO"
                },
                "postadresse": null,
                "kontakt": {
                  "epost": "firmapost@vegvesen.no",
                  "telefon": "+47 22 07 30 00",
                  "hjemmeside": "https://www.vegvesen.no"
                },
                "naeringskode": "84.130",
                "sistHentetIso": "2026-05-04T08:30:00Z"
              }
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opak cursor for neste side. `null` når listen er ferdig."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "Default 20 for søk, 50 for list, cap 100."
          },
          "fields": {
            "type": "array",
            "description": "Hvilke felter som ble returnert (etter `fields=`-filter).",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CompanyEventItem": {
        "type": "object",
        "title": "CompanyEventItem",
        "required": [
          "eventId",
          "orgnr",
          "eventType",
          "source",
          "detectedIso"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "description": "Stabil event-id på formen `{source}:{externalCursor}`. For brreg-feed:\n`brreg_enhetsregister:{oppdateringsid}`.\n",
            "pattern": "^[a-z][a-z0-9_]*:[A-Za-z0-9_.\\-]+$",
            "example": "brreg_enhetsregister:12345678"
          },
          "orgnr": {
            "type": "string",
            "pattern": "^\\d{9}$",
            "example": "974760673"
          },
          "eventType": {
            "type": "string",
            "description": "Canonical hendelsestype. For Brreg endringsfeed mappes\n`endringstype` til `brreg:enhet_opprettet|endret|slettet|fjernet`.\nUkjente verdier mappes til `brreg:enhet_ukjent` (forward-kompatibel).\n",
            "pattern": "^[a-z][a-z0-9_-]*:[a-z][a-z0-9_-]*$",
            "example": "brreg:enhet_endret"
          },
          "source": {
            "type": "string",
            "title": "SourceKey",
            "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
            "enum": [
              "brreg",
              "ssb",
              "norges-bank",
              "enhetsregister",
              "proff",
              "bygg-anlegg",
              "eksportor",
              "kartverket",
              "sodir",
              "ais",
              "artskart",
              "avinor",
              "barentswatch",
              "elhub",
              "fhi",
              "finanstilsynet",
              "fisheries",
              "fri-nettleie",
              "frost",
              "geonorge",
              "hoyesterett",
              "konkurs",
              "kommunedatabasen",
              "kostra",
              "lakselus",
              "lovtidend",
              "luftkvalitet",
              "lysingsblad",
              "mattilsynet-smilefjes",
              "met",
              "nav",
              "nb-dhlab",
              "norske-utslipp",
              "nve",
              "nvdb",
              "oslobors",
              "patentstyret",
              "politiloggen",
              "regjeringen",
              "rss-news",
              "sdir",
              "stortinget",
              "stromprise",
              "udir",
              "connectors",
              "data-quality",
              "audit",
              "companies",
              "persons",
              "kg",
              "sync",
              "config",
              "sync-state",
              "storage",
              "discovery",
              "lineage",
              "backup",
              "metrics",
              "dashboard",
              "brreg-endringer",
              "nav-grunnbelop"
            ]
          },
          "detectedIso": {
            "type": "string",
            "minLength": 20,
            "description": "ISO-8601 tidsstempel da event ble observert/skrevet til warehouse.",
            "example": "2026-05-14T08:30:00.000Z"
          },
          "rawEndringstype": {
            "type": [
              "string",
              "null"
            ],
            "description": "Råverdi fra kilde-API. For Brreg: `\"Ny\" | \"Endring\" | \"Sletting\" | \"Fjernet\"`.\n`null` hvis kilden ikke leverer en endringstype.\n",
            "example": "Endring"
          },
          "externalCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Kilde-spesifikk monotonisk cursor. For Brreg: `oppdateringsid` som string.\nBrukes internt til pagination — ikke en del av offentlig API-kontrakt.\n",
            "example": "12345678"
          }
        }
      },
      "PaginatedCompanyEvents": {
        "type": "object",
        "title": "PaginatedCompanyEvents",
        "required": [
          "items",
          "next_cursor",
          "limit",
          "orgnr"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "title": "CompanyEventItem",
              "required": [
                "eventId",
                "orgnr",
                "eventType",
                "source",
                "detectedIso"
              ],
              "properties": {
                "eventId": {
                  "type": "string",
                  "description": "Stabil event-id på formen `{source}:{externalCursor}`. For brreg-feed:\n`brreg_enhetsregister:{oppdateringsid}`.\n",
                  "pattern": "^[a-z][a-z0-9_]*:[A-Za-z0-9_.\\-]+$",
                  "example": "brreg_enhetsregister:12345678"
                },
                "orgnr": {
                  "type": "string",
                  "pattern": "^\\d{9}$",
                  "example": "974760673"
                },
                "eventType": {
                  "type": "string",
                  "description": "Canonical hendelsestype. For Brreg endringsfeed mappes\n`endringstype` til `brreg:enhet_opprettet|endret|slettet|fjernet`.\nUkjente verdier mappes til `brreg:enhet_ukjent` (forward-kompatibel).\n",
                  "pattern": "^[a-z][a-z0-9_-]*:[a-z][a-z0-9_-]*$",
                  "example": "brreg:enhet_endret"
                },
                "source": {
                  "type": "string",
                  "title": "SourceKey",
                  "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
                  "enum": [
                    "brreg",
                    "ssb",
                    "norges-bank",
                    "enhetsregister",
                    "proff",
                    "bygg-anlegg",
                    "eksportor",
                    "kartverket",
                    "sodir",
                    "ais",
                    "artskart",
                    "avinor",
                    "barentswatch",
                    "elhub",
                    "fhi",
                    "finanstilsynet",
                    "fisheries",
                    "fri-nettleie",
                    "frost",
                    "geonorge",
                    "hoyesterett",
                    "konkurs",
                    "kommunedatabasen",
                    "kostra",
                    "lakselus",
                    "lovtidend",
                    "luftkvalitet",
                    "lysingsblad",
                    "mattilsynet-smilefjes",
                    "met",
                    "nav",
                    "nb-dhlab",
                    "norske-utslipp",
                    "nve",
                    "nvdb",
                    "oslobors",
                    "patentstyret",
                    "politiloggen",
                    "regjeringen",
                    "rss-news",
                    "sdir",
                    "stortinget",
                    "stromprise",
                    "udir",
                    "connectors",
                    "data-quality",
                    "audit",
                    "companies",
                    "persons",
                    "kg",
                    "sync",
                    "config",
                    "sync-state",
                    "storage",
                    "discovery",
                    "lineage",
                    "backup",
                    "metrics",
                    "dashboard",
                    "brreg-endringer",
                    "nav-grunnbelop"
                  ]
                },
                "detectedIso": {
                  "type": "string",
                  "minLength": 20,
                  "description": "ISO-8601 tidsstempel da event ble observert/skrevet til warehouse.",
                  "example": "2026-05-14T08:30:00.000Z"
                },
                "rawEndringstype": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Råverdi fra kilde-API. For Brreg: `\"Ny\" | \"Endring\" | \"Sletting\" | \"Fjernet\"`.\n`null` hvis kilden ikke leverer en endringstype.\n",
                  "example": "Endring"
                },
                "externalCursor": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Kilde-spesifikk monotonisk cursor. For Brreg: `oppdateringsid` som string.\nBrukes internt til pagination — ikke en del av offentlig API-kontrakt.\n",
                  "example": "12345678"
                }
              }
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque base64url-encoded cursor for neste side. `null` når listen er\nferdig. Klient skal sende cursoren videre uten å forsøke å parse den.\n",
            "example": "eyJvIjoxMjM0NTY3Nn0"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "Antall items per side. Default 50, cap 100. Echoes inn-parameter slik\nat klient kan verifisere at serveren respekterte verdien.\n",
            "example": 50
          },
          "orgnr": {
            "type": "string",
            "pattern": "^\\d{9}$",
            "description": "Echoes path-parameteren slik at responsen er self-contained.",
            "example": "974760673"
          }
        }
      },
      "Problem": {
        "type": "object",
        "title": "Problem",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri-reference",
            "default": "about:blank",
            "description": "Stabil identifikator for feiltypen.",
            "example": "https://data.ketl.no/errors/rate-limit-exceeded"
          },
          "title": {
            "type": "string",
            "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
          },
          "status": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "Konkret beskrivelse av denne instansen."
          },
          "instance": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri-reference",
            "description": "URI til denne instansen av feilen (f.eks. en request-id)."
          },
          "ketlRequestId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
          },
          "ketlRetryAfterMs": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
          }
        },
        "example": {
          "type": "https://data.ketl.no/errors/rate-limit-exceeded",
          "title": "Rate limit exceeded",
          "status": 429,
          "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
          "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
          "ketlRetryAfterMs": 60000
        }
      },
      "Health": {
        "type": "object",
        "title": "Health",
        "required": [
          "status",
          "version",
          "timestamp"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "down"
            ]
          },
          "version": {
            "type": "string",
            "description": "Build-SHA + tidstempel (`<sha>-<iso>`).",
            "example": "c36f542-2026-05-04T12:00:00Z"
          },
          "timestamp": {
            "type": "string",
            "minLength": 20,
            "description": "ISO-8601 server-tid."
          },
          "upstream": {
            "type": "object",
            "description": "Status for hver upstream-avhengighet.",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "ok",
                "degraded",
                "down",
                "unknown"
              ]
            },
            "example": {
              "service": "ok",
              "firestore": "ok",
              "stripe": "ok"
            }
          },
          "diagnostics": {
            "type": [
              "object",
              "null"
            ],
            "description": "ketl-spesifikke diagnostiske felt (utenfor offentlig RFC-kontrakt).\nBrukes av admin-UI og uptime-bots for å se proxy-tilstand. Kan\ninneholde proxyBaseUrl, proxyKnownDead, proxyKnownDeadIssue (#414).\n",
            "additionalProperties": true,
            "example": {
              "proxyBaseUrl": "https://data.ketl.no",
              "proxyBaseUrlSource": "prod-default",
              "proxyKnownDead": false,
              "proxyKnownDeadIssue": null
            }
          }
        }
      }
    },
    "parameters": {
      "OrgnrPath": {
        "name": "orgnr",
        "in": "path",
        "required": true,
        "description": "Norsk organisasjonsnummer (9 siffer, MOD-11-validert).",
        "schema": {
          "type": "string",
          "pattern": "^\\d{9}$",
          "example": "974760673"
        }
      },
      "SourceKeyPath": {
        "name": "kilde",
        "in": "path",
        "required": true,
        "description": "Kilde-nøkkel (kebab-case). Se `Source.key` for full liste.",
        "schema": {
          "type": "string",
          "title": "SourceKey",
          "description": "Kebab-case kilde-nøkkel. Service-siden bruker snake_case-aliaser.",
          "enum": [
            "brreg",
            "ssb",
            "norges-bank",
            "enhetsregister",
            "proff",
            "bygg-anlegg",
            "eksportor",
            "kartverket",
            "sodir",
            "ais",
            "artskart",
            "avinor",
            "barentswatch",
            "elhub",
            "fhi",
            "finanstilsynet",
            "fisheries",
            "fri-nettleie",
            "frost",
            "geonorge",
            "hoyesterett",
            "konkurs",
            "kommunedatabasen",
            "kostra",
            "lakselus",
            "lovtidend",
            "luftkvalitet",
            "lysingsblad",
            "mattilsynet-smilefjes",
            "met",
            "nav",
            "nb-dhlab",
            "norske-utslipp",
            "nve",
            "nvdb",
            "oslobors",
            "patentstyret",
            "politiloggen",
            "regjeringen",
            "rss-news",
            "sdir",
            "stortinget",
            "stromprise",
            "udir",
            "connectors",
            "data-quality",
            "audit",
            "companies",
            "persons",
            "kg",
            "sync",
            "config",
            "sync-state",
            "storage",
            "discovery",
            "lineage",
            "backup",
            "metrics",
            "dashboard",
            "brreg-endringer",
            "nav-grunnbelop"
          ]
        }
      },
      "EndepunktPath": {
        "name": "endepunkt",
        "in": "path",
        "required": true,
        "description": "Ressurs-path inn mot kilden. Format og lovlige verdier varierer per\nkilde — se [docs/DATA_PROXY.md](https://github.com/andreas-t-hjertaker/ketl-innsiktsmaskin-web/blob/main/docs/DATA_PROXY.md).\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "example": "selskap/974760673"
        }
      }
    },
    "responses": {
      "Problem": {
        "description": "Standardisert feil per RFC 9457.",
        "content": {
          "application/problem+json": {
            "schema": {
              "type": "object",
              "title": "Problem",
              "required": [
                "type",
                "title",
                "status"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "format": "uri-reference",
                  "default": "about:blank",
                  "description": "Stabil identifikator for feiltypen.",
                  "example": "https://data.ketl.no/errors/rate-limit-exceeded"
                },
                "title": {
                  "type": "string",
                  "description": "Kort menneskelig oppsummering. Skal IKKE endres mellom requests."
                },
                "status": {
                  "type": "integer",
                  "minimum": 100,
                  "maximum": 599
                },
                "detail": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Konkret beskrivelse av denne instansen."
                },
                "instance": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri-reference",
                  "description": "URI til denne instansen av feilen (f.eks. en request-id)."
                },
                "ketlRequestId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Korrelerings-ID. Send tilbake ved support-henvendelse."
                },
                "ketlRetryAfterMs": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "description": "Antall millisekunder klient bør vente før retry (alternativ til Retry-After-header for fetch-baserte klienter)."
                }
              },
              "example": {
                "type": "https://data.ketl.no/errors/rate-limit-exceeded",
                "title": "Rate limit exceeded",
                "status": 429,
                "detail": "Du har gjort 1001 calls i siste 60 sekunder; tier `pro` tillater 1000.",
                "ketlRequestId": "req_01HZN3R5QKV8MQX7K9C2W6J5RT",
                "ketlRetryAfterMs": 60000
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ketl-api-key",
        "description": "API-nøkkel utstedt fra `/dashboard/utvikler`. Format `kpk_*` (Pro/Team)\neller `ksk_*` (Enterprise). Send som `Authorization: Bearer <key>`.\n"
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ]
}
