API Reference
The API is served on two versions with different authentication:
/api/v1/*— Data endpoints authenticated with theX-API-KEYheader./api/v2/*— The same data endpoints authenticated with a HelseID access token (DPoP).
The health check (/api/internal/health) requires no authentication. See Authentication.
GET /api/v1/treatment-group
Returns treatment group data. Supports filtering and version-based diff queries.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since-version |
long |
No | If provided, returns only items changed since this version. If omitted, returns all active items. |
name |
string |
No | Returns active treatment groups with matching name. |
disease-group |
string |
No | Returns active treatment groups that contains the disease group. |
jurisdiction |
string |
No | Returns active treatment groups that contains the jurisdiction code. |
indication |
string |
No | Returns active treatment groups that contains the indication code. |
vare-nr |
string |
No | Returns active treatment groups that contains the FEST/Varenummer. |
effective-date |
string YYYY-MM-DD |
No | Returns treatment groups that is active on the given date. |
Response
{
"currentVersion": 5,
"entries": [
{
"data": { ... },
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
]
}
Without since-version: Returns all non-deleted items. isDeleted is always false.
With since-version: Returns items with version > N, including deleted items. isDeleted may be true for items that were removed from the source system.
Example
# Get all
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group
# Get changes since version 3
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group?since-version=3
# Filter on indication code
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group?indication=G35
GET /api/v1/treatment-group/
Returns a treatment group.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Returns an active treatment group with matching id. |
Response
{
"data": {
"id": "...",
"name": "...",
"description": "...",
"diseaseGroup": "...",
"jurisdictions": [ { "system": "...", "code": "100022", "display": "Helse Nord RHF" } ],
"validityPeriod": { "start": "...", "end": "..." },
"reviewDate": "...",
"indications": [ { "coding": [ {"system": "...", "code": "C500","display": "..."}], "text": null } ],
"treatmentAlternatives": [
{
"rank": 1,
"treatmentId": "...",
"name": "Atezolizumab (Tecentriq)",
"description": "...",
"products": [
{
"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" }
]
}
]
}
]
},
"version": 1,
"lastChanged": "2026-06-23T09:17:13.113Z",
"isDeleted": false
}
GET /api/v1/reimbursement-group
Returns reimbursement group data (legal basis, indications, covered products). Supports filtering and version-based diff queries.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
since-version |
long |
No | If provided, returns only items changed since this version. If omitted, returns all active items. |
indication |
string |
No | Returns active reimbursement groups that contains the indication code. |
Response
{
"currentVersion": 5,
"entries": [
{
"data": {
"id": "...",
"legalBasis": { "coding": [ { "system": "...", "code": "950", "display": "H-resept" } ] },
"indications": [ { "coding": [ { "system": "...", "code": "G35", "display": "Multiple sclerosis" } ] } ],
"products": [
{
"identifiers": [
{ "system": "FEST/LegemiddelPakning/Varenummer", "value": "166028" },
{ "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" },
{ "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" }
]
}
]
},
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
]
}
Behavior is identical to the treatment-group endpoint. The products array is empty if upstream provides no article number for the authorization.
Example
# Get all
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group
# Get changes since version 3
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group?since-version=3
# Filter on indication code
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/reimbursement-group?indication=G35
GET /api/v1/reimbursement-group/
Returns a reimbursement group (legal basis, indications, covered products).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Returns an active reimbursement group with matching id. |
Response
{
"data": {
"id": "...",
"legalBasis": { "coding": [ { "system": "...", "code": "950", "display": "H-resept" } ] },
"indications": [ { "coding": [ { "system": "...", "code": "G35", "display": "Multiple sclerosis" } ] } ],
"products": [
{
"identifiers": [
{ "system": "FEST/LegemiddelPakning/Varenummer", "value": "166028" }
]
}
]
},
"version": 5,
"lastChanged": "2025-04-10T08:30:00Z",
"isDeleted": false
}
HelseID (v2) endpoints
The v2 endpoints expose the same data and behave identically to their v1 counterparts. The differences are:
- Authentication — a HelseID access token sent with a DPoP proof, instead of the
X-API-KEYheader. See HelseID (DPoP). - No rate limiting — v2 endpoints are not part of the per-API-key rate limiter described below.
Response shapes, query parameters, and versioned-diff semantics are identical to the v1 endpoints above.
GET /api/v2/treatment-group
Returns treatment group data. Supports the same filtering and version-based diff queries as GET /api/v1/treatment-group.
Parameters
Identical to GET /api/v1/treatment-group (since-version, name, disease-group, jurisdiction, indication, vare-nr, effective-date).
Response
Same envelope and entry shape as GET /api/v1/treatment-group.
Example
# Get all (authenticated with a HelseID DPoP access token + proof)
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/treatment-group
# Get changes since version 3
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/treatment-group?since-version=3
GET /api/v2/treatment-group/
Returns a single treatment group. Same id parameter and response shape as GET /api/v1/treatment-group/{id}.
GET /api/v2/reimbursement-group
Returns reimbursement group data. Supports the same filtering and version-based diff queries as GET /api/v1/reimbursement-group.
Parameters
Identical to GET /api/v1/reimbursement-group (since-version, indication).
Response
Same envelope and entry shape as GET /api/v1/reimbursement-group.
Example
curl -H "Authorization: DPoP <access-token>" \
-H "DPoP: <proof-jwt>" \
https://api.example.com/api/v2/reimbursement-group?indication=G35
GET /api/v2/reimbursement-group/
Returns a single reimbursement group. Same id parameter and response shape as GET /api/v1/reimbursement-group/{id}.
Response Envelope
All endpoints return data in the same envelope:
{
currentVersion: number; // The server's current data version
entries: Array<{
data: T; // The actual data object
version: number; // Version when this entry last changed
lastChanged: string; // ISO-8601 timestamp of last change
isDeleted: boolean; // Whether this entry has been removed
}>;
}
GET /api/internal/health
Returns the health status of the API and its dependencies. This endpoint does not require authentication and is intended for load balancer and Kubernetes liveness/readiness probes.
Response
{
"status": "healthy",
"timestamp": "2025-04-10T08:30:00Z",
"components": {
"postgres": "healthy"
}
}
When a dependency is unhealthy:
{
"status": "unhealthy",
"timestamp": "2025-04-10T08:30:00Z",
"components": {
"postgres": {
"status": "unhealthy",
"error": "Connection refused"
}
}
}
Status Codes
| Code | Meaning |
|---|---|
200 OK |
All dependencies are healthy |
503 ServiceUnavailable |
One or more dependencies are unhealthy |
Example
curl http://localhost:5069/api/internal/health
HTTP Status Codes
| Code | Meaning |
|---|---|
200 OK |
Success |
400 Bad Request |
Missing or invalid parameter |
401 Unauthorized |
Missing or invalid API key, or invalid/expired/replayed HelseID token or DPoP proof |
403 Forbidden |
Authenticated via HelseID, but the access token is missing the required nhn:nompd/api scope |
404 Not found |
Data not found in DB |
429 Too Many Requests |
Rate limit exceeded (v1 endpoints only, see Rate Limiting) |
500 Internal Server Error |
Server error |
503 ServiceUnavailable |
Health check detected unhealthy dependency |
Error Format
Unhandled server errors return 500 Internal Server Error with the following JSON body:
{
"correlationId": "3f2b1c9a-4d5e-6f70-8192-a3b4c5d6e7f8"
}
In the Development environment, the body also includes message and stackTrace.
Every response — successful or error — includes an X-Correlation-Id response header. On 500 responses the same value is included in the body as correlationId. Clients may pass their own correlation ID via the X-Correlation-Id request header; it will be reused in the response and in server logs.
If the client aborts the request, no error response is written.
Rate Limiting
The data endpoints (/api/v1/treatment-group, /api/v1/reimbursement-group, /api/internal/sync) are rate limited to 120 requests per minute per API key (sliding window). The limit is configurable via the RateLimiting__PermitLimit, RateLimiting__WindowSeconds, and RateLimiting__QueueLimit environment variables. Rate limiting can be disabled entirely with RateLimiting__Enabled=false (default true).
The v2 (HelseID) endpoints are not rate limited.
When the limit is exceeded, the API responds with 429 Too Many Requests:
{ "error": "Rate limit exceeded. Try again later." }
The response includes a Retry-After header with the length of the rate-limit window in seconds. Waiting that long guarantees the request budget has fully replenished.
Notes:
- Requests without a valid API key (missing or unknown) share a single
anonymousbucket, which also prevents API-key brute-forcing. - The limit is enforced per replica (in-memory). With 3 replicas in production, the effective fleet-wide limit is ~3x the configured value.
RateLimiting__Enabled=falsedisables rate limiting on the data endpoints (requests pass through unthrottled). The flag is read at startup.GET /api/internal/healthand the Swagger UI are not rate limited.