KRS Practitioner Replication

KRS Practitioner Replication

Practitioner Replication (CCR0297)

Overview

In addition to citizen (patient) replication from CPR/Sikrede, fut-krs-client also replicates
healthcare practitioner data from two SKRS registers maintained on NSP:

  • Autorisationsregisteret — the Danish Health Authority's register of healthcare professional
    authorizations (læger, sygeplejersker, fysioterapeuter, etc.)

  • Yderregisteret — SDSD's register of general practitioners (ydere) and their affiliated
    personnel (yderpersoner)

The goal is to keep FHIR Practitioner resources in the Organization Service up to date with the
latest authorization and provider affiliation data from the national registers, without requiring
manual maintenance.

Practitioner replication is controlled by a single PractitionerReplicationRunner that coordinates
the two phases: authorization replication always runs first, and provider (yder) replication only
runs when authorization is fully caught up within the current transaction budget. This ordering
preserves the invariant that yder data enriches practitioners that already exist — practitioners
are created by authorization replication, never by yder replication.

CPR-to-Keycloak-UUID resolution

Both SKRS registers identify practitioners by CPR number, while FHIR resources in FUT are
identified by their Keycloak UUID (the kc-uuid identifier). The kc-uuid is the stable internal
user identity used across all FUT services.

During replication, CPR numbers are resolved to Keycloak UUIDs by calling the kc-uuid API on
fut-keycloak. The API either returns an existing mapping or creates a new UUID entry for
previously unseen CPR numbers. The base URL for the kc-uuid API is derived from
ehealth.client.authz.url and ehealth.client.authz.realm:

<ehealth.client.authz.url>/realms/<ehealth.client.authz.realm>/kc-uuid/batch-map-cpr-to-uuid

Authorization register integration

Source register: autorisationsregisteret / datatype autorisation (SKRS version 5)
NSP reference: https://www.nspop.dk/spaces/Web3/pages/94656873/B02+Autorisations+registret

The authorization register contains one record per healthcare professional authorization. A single
person may hold multiple authorizations (e.g. both nurse and midwife). Each record includes:

SKRS field

Description

Mapped to FHIR

SKRS field

Description

Mapped to FHIR

autorisationsnummer

Authorization identifier

Practitioner.qualification.identifier

cpr

Practitioner CPR number

Used for kc-uuid resolution only

fornavn

First name

Practitioner.name.given

efternavn

Last name

Practitioner.name.family

uddannelseskode

Profession group code (DK Core)

Practitioner.qualification.code

autorisationGyldig

Whether the authorization is valid

Affects qualification period end date

validFrom

Record validity start (SKRS replication field)

Practitioner.qualification.period.start

validTo

Record validity end (SKRS replication field)

Practitioner.qualification.period.end

When practitioners are created or updated from authorization data:

  • A Practitioner is created when the CPR resolves to a UUID that has no existing Practitioner
    resource in the Organization Service.

  • A Practitioner is updated when a resource already exists for the resolved UUID. Only the
    name and qualifications are written — other attributes are not touched.

  • Records with an expired validTo (SKRS replication validity, not the authorization period) are
    filtered out before processing.

  • Records without an uddannelseskode are skipped because the DK Core ehealth-practitioner
    profile requires a qualification code.

  • If a CPR number cannot be resolved to a UUID (the kc-uuid API returns no result), the record is
    skipped and a debug log entry is written.

  • Qualification deduplication: An authorization qualification is only added to a Practitioner if
    no existing qualification already carries the same autorisationsnummer identifier. This prevents
    duplicates across replication runs.

  • Qualification period: validFrom maps to period.start. validTo maps to period.end.
    If validTo is absent but autorisationGyldig is "0" or "false", period.end is set to
    the current timestamp to indicate a revoked authorization.


Yder register integration

Source register: yderregister — two datatypes processed in sequence:

Datatype

SKRS identifier key

Purpose

Datatype

SKRS identifier key

Purpose

yder

ydernryder

Yder (clinic) records, including activation/deactivation dates

person

ydernrperson

Person-yder affiliation links (CPR → ydernr)

NSP reference: https://www.nspop.dk/spaces/Web3/pages/99445253/B25+Yderregisteret

Yder fields (yder datatype):

SKRS field

Description

Mapped to FHIR

SKRS field

Description

Mapped to FHIR

YdernrYder

Yder number (padded to 6 digits)

Practitioner.extension[yder].identifier

TilgDatoYder

Yder registration date

Not directly mapped

AfgDatoYder

Yder deregistration/closure date

Practitioner.extension[yder].period.end

ValidFrom

Record validity start (SKRS field)

Used for filtering only

ValidTo

Record validity end (SKRS field)

Used for filtering only

YderPerson fields (person datatype):

SKRS field

Description

Mapped to FHIR

SKRS field

Description

Mapped to FHIR

YdernrPerson

Yder number the person is affiliated with

Practitioner.extension[yder].identifier

CprNr

Practitioner CPR number

Used for kc-uuid resolution only

ValidFrom

Link validity start (SKRS field)

Practitioner.extension[yder].period.start

ValidTo

Link validity end (SKRS field)

Practitioner.extension[yder].period.end

The yder FHIR extension is at:
<http://ehealth.sundhed.dk/fhir/StructureDefinition/ehealth-practitioner-yder>

When practitioners are updated from yder data:

  • Yder replication does not create new Practitioner resources. It only enriches practitioners
    that were created by authorization replication.

  • If no existing Practitioner is found for a resolved UUID, the yderperson record is normally
    skipped. Exception: if the most recent validFrom date among the person's yder links is
    within provider.replication.missingPractitionerPauseDays days (default: 2), replication pauses at the current offset and does not advance. This allows authorization replication to create the missing Practitioner on the next run before yder data is applied.

  • Each yder affiliation is stored as a separate yder extension entry on the Practitioner,
    identified by ydernr. Existing entries for the same ydernr are updated in place; new ydernr
    entries are added.

  • period.start is set from validFrom on the yderperson link.

  • period.end is set from AfgDatoYder on the yder record (clinic deactivation) if present,
    otherwise from validTo on the yderperson link.

  • When a yder is deactivated (AfgDatoYder set), all practitioners associated with that ydernr
    have their period.end updated regardless of yderperson replication progress.


Error handling

Situation

Behaviour

Situation

Behaviour

SKRS SOAP fault or connection error

ReplicationJobException propagates; the job fails and is retried on the next scheduled run. The offset is not advanced, so no data is lost.

kc-uuid API returns no result for a CPR

Record is skipped; logged at DEBUG level.

Authorization record without uddannelseskode

Record is skipped; logged at WARN level.

FHIR version conflict on update (HTTP 409)

Practitioner is re-fetched by UUID and the update is retried once. If it fails again, the error is logged at ERROR level and the next practitioner is processed.

Offset not advancing after a SKRS page fetch

ReplicationJobException is thrown to prevent an infinite loop.

Yderperson references a practitioner that does not yet exist with a recent validFrom

Replication pauses at the current offset until the next run. Authorization replication has a chance to create the practitioner first.

Any other exception during create/update of a single practitioner

Error is logged and the next practitioner is processed. The offset is still advanced at end of page.