Publisert - 04.09.2026

Developer Documentation

Documentation for API consumers integrating with the Nompd API.

Contents

  • Getting Started — Quickstart guide with curl examples to get up and running
  • API Reference — Complete endpoint documentation with parameters, responses, and examples (v1 API-key and v2 HelseID endpoints)
  • Authentication — How to authenticate using API keys (v1) or HelseID with DPoP (v2) and expected error responses
  • Versioning and Diff — How to use version-based differential sync to fetch only changes
  • Data Models — Full schema documentation for TreatmentGroup, ReimbursementGroup, and related types
  • Changelog — Release notes and version history
  • Working with Agents — How to connect AI agents such as Opencode to GitLab Milliways and Azure DevOps via MCP
  • Code Flow Through Environments — GitLab Flow branch strategy for main, test, and prod: feature promotion, patch/hotfix sync-back, and commit conventions
Publisert - 15.09.2026

Changelog

All notable changes to the Nompd API will be documented in this file.

The format is based on Keep a Changelog.

[Unreleased]

Breaking Changes

  • New product structure — The products entries in both treatment-group and reimbursement-group responses (v1 and v2) have a new shape. The reference, type, and display fields are removed, and the single identifier object is replaced by an identifiers list:

    {
      "identifiers": [
        { "system": "FEST/LegemiddelPakning/Varenummer", "value": "448992" },
        { "system": "FEST/LegemiddelPakning/Id", "value": "ADDED_WHEN_FEST_FILE_IS_READ" },
        { "system": "FEST/LegemiddelMerkevare/Id", "value": "ADDED_WHEN_FEST_FILE_IS_READ" },
        { "system": "FEST/LegemiddelVirkestoff/Id", "value": "ADDED_WHEN_FEST_FILE_IS_READ" }
      ]
    }
    

    The identifier system is renamed from FEST/Varenummer to FEST/LegemiddelPakning/Varenummer (the previous type value is folded into the system string). The FEST/*/Id identifiers carry the placeholder value ADDED_WHEN_FEST_FILE_IS_READ until FEST file ingestion populates the real values. products are empty between the deployment of this change and the next sync.

  • Renamed items to entries in the response envelope — The list endpoints (treatment-group and reimbursement-group, v1 and v2) now return { "currentVersion": ..., "entries": [...] } instead of { "currentVersion": ..., "items": [...] }. The entry shape (data, version, lastChanged, isDeleted) is unchanged. No backwards-compatible parallel field — consumers must migrate.

Added

  • HelseID (DPoP) v2 endpoints — The data endpoints are now also served under /api/v2/, authenticated with a HelseID access token using DPoP (RFC 9449) instead of the X-API-KEY header:

    • GET /api/v2/treatment-group, GET /api/v2/treatment-group/{id}
    • GET /api/v2/reimbursement-group, GET /api/v2/reimbursement-group/{id}
    • Same data, query parameters, and response shapes as the v1 endpoints.
    • Requests send Authorization: DPoP <access-token> plus a per-request DPoP proof header. The access token must be issued by HelseID with audience nhn:nompd and the nhn:nompd/api scope. Bearer tokens are not accepted on v2 endpoints (they return 401).
    • New status code: 403 Forbidden when authenticated via HelseID but the access token is missing the nhn:nompd/api scope.
    • v2 endpoints are not rate limited.
    • Non-breaking — v1 endpoints and API key authentication are unchanged.
  • Rate limiting — The data endpoints (/api/v1/treatment-group, /api/v1/reimbursement-group, /api/internal/sync) are now rate limited to 120 requests per minute per API key (sliding window, configurable via RateLimiting__* environment variables). Exceeding the limit returns 429 Too Many Requests with a Retry-After header (set to the rate-limit window length) and body { "error": "Rate limit exceeded. Try again later." }. Requests without a valid API key (missing or unknown) share an anonymous bucket, keeping the number of limiter partitions bounded. Limiting can be disabled with RateLimiting__Enabled=false. The health endpoint and Swagger UI are not rate limited. Not a breaking change — only new 429 responses under load.

  • Global exception handling — Unhandled exceptions now return 500 Internal Server Error with a JSON body containing a correlationId. In the Development environment, the response also includes message and stackTrace. The correlation ID is reused from an incoming X-Correlation-Id header when present, otherwise generated, and is echoed in the X-Correlation-Id response header. Exceptions are logged with their correlation ID.

  • X-Correlation-Id on all responses — Every response (not only 500 errors) now includes an X-Correlation-Id response header, so any request can be traced in the server logs. A client-provided X-Correlation-Id request header is still reused when present. Non-breaking — only adds a response header.

Removed

  • MongoDB deployment and helper scripts — Removed the MongoDB Helm chart values (manifests/apps/mongodb), the mongodb entries from the ArgoCD app-of-apps values, and the helper scripts create-mongodb-secret.sh, forward-db.sh, and run-exporer.sh. PostgreSQL is the sole datastore. No API behavior or data contract changes.

Fixed

  • Access token typ validation on v2 (HelseID) endpoints — Access tokens whose typ header is not at+jwt are now rejected with 401 Unauthorized, as required by the HelseID security profile. Previously the typ header was not validated, so such tokens were accepted and the request was processed.
  • DPoP htu validation behind TLS-terminating gateway — The forwarded-headers middleware now forwards X-Forwarded-Proto and trusts only the source networks configured via FORWARDED_TRUSTED_NETWORKS (comma-separated CIDR list) instead of all proxies. This ensures request.Scheme is https for DPoP htu comparison while preventing header spoofing from untrusted sources.
  • Swallowed unhandled exceptions — The request logging middleware previously swallowed unhandled exceptions, which resulted in empty 200 OK responses with no error log. Exceptions now propagate to the exception handler.
  • Client aborts — A client aborting a request no longer produces a 500 response or an error log.

Breaking Changes

  • Removed /filter sub-route — Filter parameters are now query params on the main endpoint.
    • GET /api/v1/treatment-group/filter?...GET /api/v1/treatment-group?...
    • GET /api/v1/reimbursement-group/filter?...GET /api/v1/reimbursement-group?...
    • No backwards-compatible parallel endpoint — consumers must migrate.

[4.0.0] - 2026-06-17

Breaking Changes

  • Renamed medicines to products on both TreatmentAlternative and ReimbursementGroup, and renamed the referenced type MedicinalReference to ProductReference.
    • JSON field: medicinesproducts (affects GET /api/v1/treatmentGroup, GET /api/v1/reimbursementGroup, GET /api/v1/reimbursementGroup/{id}, and GET /api/v1/reimbursementGroup/filter)
    • The element shape (reference, type, identifier, display) is unchanged — only the array field name and the C# type name changed.
    • No backwards-compatible parallel field — consumers must migrate.

[3.1.0] - 2026-05-07

Added

  • medicines on ReimbursementGroup — exposes the medicines covered by a reimbursement authorization. Sourced from RegulatedAuthorization.ArticleNumber; emitted as a MedicinalReference[] (FEST/Varenummer). Empty list when upstream provides no article number. Non-breaking additive change.

[3.0.0] - 2026-04-30

Breaking Changes

  • Renamed Behandlingsgruppe model to TreatmentGroup — endpoint, model, and database table all renamed for consistency with ReimbursementGroup.
    • Endpoint: GET /api/v1/behandlingsgrupperGET /api/v1/treatmentGroup
    • Table: BehandlingsgrupperTreatmentGroups
    • Model shape unchanged (same fields: id, name, description, diseaseGroup, jurisdictions, validityPeriod, reviewDate, indications, treatmentAlternatives)
    • No backwards-compatible parallel endpoint — consumers must migrate.

Added

  • Per-document version metadata in response wrapperVersionedItem<T> now exposes version (the sync version when this item last changed) and lastChanged (ISO-8601 timestamp). Applies to both treatmentGroup and reimbursementGroup endpoints.

[2.0.0] - 2026-04-28

Breaking Changes

  • Replaced Reimbursement model with ReimbursementGroup — the endpoint, model shape, and database table have all been renamed and restructured.

    • Endpoint: GET /api/v1/reimbursementsGET /api/v1/reimbursementGroup
    • Table: ReimbursementsReimbursementGroups
    • Model is reduced to three fields: id, legalBasis (CodeableConcept), indications (CodeableConcept[])
    • The previous medicinalReferences array is removed pending a redesign
    • legalBasis now correctly carries the hjemmel (e.g., H-resept §950) sourced from the upstream reimbursementRegulation extension, rather than the first coding from Basis
    • indications carries the diagnoses (e.g., ICD-10 codes) sourced from Basis.Coding, with one CodeableConcept per coding

    Consumers using the old endpoint must migrate. There is no backwards-compatible parallel endpoint.

[1.1.0] - 2026-04-28

Added

  • Raw FHIR persistence — Upstream FHIR resources are now stored in Postgres alongside the converted output, sharing the same version number per sync.

    • New versioned collections: PlanDefinitions, ActivityDefinitions, RegulatedAuthorizations
    • Each wrapped in VersionedDocument<T> with the same upsert + soft-delete semantics as the output collections
    • Enables re-conversion if mapping logic changes, debugging via raw-vs-converted comparison, and full audit traceability
  • Sync history — Every sync run writes one entry to the new SyncHistory collection with:

    • Version, SyncedAt, DurationMs
    • Per-collection statistics (AddedCount, UpdatedCount, UnchangedCount, DeletedCount)
    • Business keys of every added, updated, and deleted record (AddedKeys, UpdatedKeys, DeletedKeys)
  • unchanged field in SyncResult — The POST /api/internal/sync response now includes a count of records that were detected as unchanged.

Changed

  • Content-hash-based change detectionVersionedDocument<T> has a new ContentHash field (SHA256 of BSON). Records only receive a new Version if their hash actually changed (or their deletion state flipped). Previously every sync bumped the version on every record, which made ?sinceVersion=N return everything after each sync. Now diff queries return only the records that genuinely changed.

    • Migration impact: The first sync after deploy bumps the version on all existing records once (because they have no hash to compare against), then subsequent syncs only touch genuinely changed records.
  • Sync log line — Now includes unchanged count in the structured log message.

Internal

  • PlanDefinition no longer has a [BsonId] attribute — it is now embedded inside VersionedDocument<PlanDefinition>, and the wrapper holds the database primary key.
  • UpsertCollection<T> in SyncService now returns SyncCollectionStats (counts plus business keys) instead of a counts-only tuple.

[1.0.0] - 2026-04-13

Added

  • Behandlingsgrupper endpoint (GET /api/v1/behandlingsgrupper)

    • Returns treatment groups with ranked treatment alternatives
    • Includes jurisdictions, indications, validity periods, and medicine references
  • Reimbursements endpoint (GET /api/v1/reimbursements)

    • Returns reimbursement authorizations with regulation basis and indication codes
    • Includes medicine references (FEST and NOMPD)
  • Version-based diff support

    • All endpoints accept ?since-version=N to return only changes since a given version
    • Responses include currentVersion for clients to track their sync state
    • Deleted items are included in diff responses with isDeleted: true
  • API key authentication

    • All endpoints require X-API-KEY header
    • Keys are configured server-side and issued manually
  • Sync endpoint (POST /api/internal/sync)

    • Triggers a full data sync from upstream FHIR APIs
    • Intended to be called by a Kubernetes CronJob
    • Returns sync statistics (added, updated, deleted, duration)

Søk i Utviklerportalen

Søket er fullført!