IKJ-API overview
Informasjon fra Kommunal Journal (IKJ) provides endpoints for submitting and retrieving patient information. Patient resources are represented using the FHIR standard, version R4.
Available environments
| Environment | FQDN |
|---|---|
| Test | api.ikj.test.nhn.no |
Access control
Accessing the IKJ API requires clients to be authenticated using HelseID and DPoP (Demonstrating Proof-of-Possession):
- Scope: Clients must request the appropriate scope (
nhn:ikj/api.readornhn:ikj/api.write). - Claim: Access token must include claim
helseid://claims/client/claims/orgnr_parent. - DPoP required: Only requests including DPoP tokens are accepted.
- Authorization header: Access token must be sent using the
DPoPprefix (i.e., notBearer). - DPoP-Proof: Requests must include a
DPoPheader with a signed proof generated by the client.
See HelseID Dokumentasjon - DPoP for implementation details.
Supported resource types
| Operation | Patient | EpisodeOfCare | CarePlan |
|---|---|---|---|
Submit: POST /v1/{type} |
✓ | ✓ | ✓ |
Update: PUT /v1/{type}/{resourceId} |
✓ | ✓ | ✓ |
Update: PUT /v1/{type}?identifier=... |
✓ | ||
Read: GET /v1/{type}/{resourceId} |
✓ | ✓ | ✓ |
Search: GET /v1/{type}?... |
✓ | ✓ | ✓ |
The behavior shared by all resource types is described under Common behavior. The sections for each resource type only describe what is specific to that type.
Common behavior
Submitting and updating
All write requests are processed asynchronously. A request that passes initial validation returns
202 Accepted, and the resource is queued for processing. The Location header contains the URL of a Task
resource that reports the outcome; poll the Task to find out whether processing succeeded or failed.
Errors are reported in one of two ways:
- A request that cannot be accepted — such as an unparsable body or a malformed parameter — is rejected
immediately with
400 Bad Request. - A request that is accepted but cannot be carried out — such as one referencing a resource that does not exist, or
violating an identifier rule — returns
202, and the failure is reported on the Task.
Resource IDs are assigned by the server and cannot be chosen by the client. A submitted resource's ID is not part
of the 202 response; once processing has succeeded, the Task contains a reference to the resource, such as
Patient/3f9a1c58-2d4b-4e17-8c60-7a5b9e0d1234.
The request body size limit is 900 KB (921,600 bytes).
Updating by resource ID
PUT /v1/{type}/{resourceId} replaces an existing resource.
{resourceId}must be a resource ID obtained from a Task, as described above. A value that is not a well-formed resource ID is rejected with400 Bad Request.- The request body must contain an
ididentical to{resourceId}. A missing or differentidis rejected with400 Bad Request. - A
PUTto a resource that does not exist fails during processing.
A PUT replaces the stored resource in full — it is not a merge. For identifiers, this means the stored set
becomes exactly the set in the request body: an identifier that is omitted is removed, and stops working as a
lookup key. An identifier in the body that is already registered to a different resource of the same type fails
processing — resources are never merged.
Reading
GET /v1/{type}/{resourceId} returns the resource, or 404 Not Found with an OperationOutcome if there is no
match.
Searching
GET /v1/{type}?... returns a FHIR Bundle of type searchset with the matching resources, possibly none. Matching
entries have search.mode = match. The supported search parameters for each resource type are listed in its section, and
may be combined freely; a resource must match all of them.
_lastUpdated is supported for all resource types, and limits results by when resources were last updated in IKJ.
It requires the prefix ge or gt and at least minute precision, e.g. _lastUpdated=ge2024-01-01T12:00Z.
Pagination
Search results are paged using keyset pagination:
_countcontrols the page size (1–50, default 20)._sortcontrols the sort order:_lastUpdatedfor ascending (default) or-_lastUpdatedfor descending.- When there are more results, the Bundle includes a
nextlink with an opaquecursorparameter. Follow the link viaGETto fetch the next page.
Treat the cursor as opaque — do not decode or modify it. It carries the filters, page size and sort order of the
request that produced it, so combining a cursor with filter parameters, _count or _sort is rejected with
400 Bad Request.
GET /v1/Patient?_count=10&_sort=-_lastUpdated
{
"resourceType": "Bundle",
"type": "searchset",
"link": [
{
"relation": "self",
"url": "https://api.ikj.test.nhn.no/v1/Patient?_count=10&_sort=-_lastUpdated"
},
{
"relation": "next",
"url": "https://api.ikj.test.nhn.no/v1/Patient?cursor=ZXlKTVlYTjB..."
}
]
}
Identifiers and coded elements
IKJ treats three kinds of elements differently, which decides both what you must submit and what you can search for:
- Identifiers (
Patient.identifier,EpisodeOfCare.identifier,CarePlan.identifier) name one specific resource. They are lookup keys, so they must be unambiguous. - Referenced identifiers (
EpisodeOfCare.managingOrganization.identifier) identify a different resource that this one points at. They are written and searched like identifiers, but say nothing about the identity of the resource carrying them. - Coded elements (
EpisodeOfCare.type, and otherCodeableConceptelements as they are introduced) classify a resource. Many resources legitimately share the same code.
Rules for identifiers and referenced identifiers
- Only identifiers with both a
systemand avalueare indexed. An identifier missing either part is stored in the resource as submitted, but cannot be searched for, cannot be used to update by identifier, and takes no part in the uniqueness rule below. - The
systemmay be at most 256 and thevalueat most 64 characters long. A resource carrying a longer one fails processing. - Identifiers are unique: if an identifier is already registered to a different resource of the same type in your organization, processing fails. Referenced identifiers are not unique — many EpisodeOfCare resources may name the same managing organization.
- Searching by identifier is a token search, and may return more than one resource. Only the full
{system}|{value}form of an identifier is guaranteed to match at most one. Updating by identifier requires the full form.
Rules for coded elements
systemandcodeare both optional, but a coding carrying neither is ignored, as nothing could ever match it. ACodeableConceptwith onlytextand nocodingis therefore not searchable.- Coded elements are not unique. A search by code returns every resource carrying it.
Empty values
FHIR does not allow an empty string as an element value — omit the element instead. "system": "" is invalid and
is rejected when the resource is parsed, before any of the rules above are applied.
Token search forms
Search parameters that match an identifier or a coded element are FHIR tokens. For identifiers, read {code} as
{value}:
| Form | Matches |
|---|---|
{code} |
The code in any system |
{system}\|{code} |
The code in that system |
\|{code} |
The code, only where the coding was submitted without a system |
{system}\| |
Any code in that system |
The |{code} form is only supported for coded elements: identifiers without a system are not indexed, so the form
could never match one. It is rejected with 400 Bad Request for identifier search parameters.
A token that is just |, that contains more than one |, or whose system exceeds 256 or code exceeds 64
characters, is rejected with 400 Bad Request. A parameter given with an empty value, such as type=, is treated
as if it were absent.
Patient resources
A Patient must carry at least one identifier with both a system and a value; processing of a Patient without one
fails.
Updating by identifier
PUT /v1/Patient?identifier={system}|{value} updates an existing patient found by identifier instead of resource ID.
It otherwise behaves like updating by resource ID.
- The
identifierparameter must use the full{system}|{value}form, and must match one of the identifiers in the request body; otherwise the request is rejected with400 Bad Request. Since that identifier must be in the body, it is the one identifier this endpoint cannot change — use another identifier or the resource ID to change it. - This endpoint does not create patients. If no patient in your organization matches the identifier, processing fails.
- The
idin the request body is optional. When omitted, the server fills in the ID of the patient found. When present, a mismatch with the patient found fails processing.
Search parameters
| Parameter | Description |
|---|---|
identifier |
Identifier token. All token forms except \|{value}. |
_lastUpdated |
See Searching. |
Example:
GET /v1/Patient?identifier=http://example.no/ids|12345
EpisodeOfCare resources
Submitting and updating
The patient element is required and must reference an existing patient in the form Patient/{resourceId}. A
reference of any other form is rejected with 400 Bad Request; a reference to a patient that does not exist fails
processing.
When updating:
- The
patientreference cannot be changed. It must still be present, and name the patient the EpisodeOfCare was created for; a different patient fails processing. managingOrganizationis replaced like the identifiers — omitting it removes the stored one.statusis required.periodis replaced by the value in the body, or cleared if the body omits it.
Search parameters
| Parameter | Description |
|---|---|
patient |
Resource ID of the patient. |
patient.identifier |
Identifier token of the patient. All token forms except \|{value}. |
type |
Service type token. All four token forms. Matches if any coding of any type entry matches. |
managingOrganization.identifier |
Identifier token of the managing organization. All token forms except \|{value}. |
_lastUpdated |
See Searching. |
When patient and patient.identifier are combined, both must match the same patient. Example:
GET /v1/EpisodeOfCare?patient.identifier=urn:oid:2.16.578.1.12.4.1.4.1|12345678901&type=http://example.no/codes|ABC
_include=EpisodeOfCare:patient adds the referenced Patient to the response, with search.mode = include. It is
supported both when searching, where each patient is included once per page, and when reading by resource ID, where
the response becomes a searchset Bundle instead of a single EpisodeOfCare. Any other _include value is rejected
with 400 Bad Request.
CarePlan resources
Submitting and updating
The subject element is required and must reference an existing patient in the form Patient/{resourceId}. A
reference of any other form is rejected with 400 Bad Request; a reference to a patient that does not exist fails
processing.
When updating:
- The
subjectreference cannot be changed. It must still be present, and name the patient the CarePlan was created for; a different patient fails processing. - Activities are replaced like the identifiers — an activity omitted from the body is removed.
Search parameters
| Parameter | Description |
|---|---|
activity-date |
Scheduled period of an activity. See below. |
patient |
Resource ID of the patient referenced by subject. |
patient.identifier |
Identifier token of the patient. All token forms except \|{value}. |
_lastUpdated |
See Searching. |
When patient and patient.identifier are combined, both must match the same patient.
activity-date matches CarePlan resources with at least one activity whose scheduled period overlaps the given
date criteria. Only schedules of type Period are supported. The parameter can be repeated, in which case a single
activity must satisfy all criteria. It requires minute precision, and supports the prefixes eq, gt, ge, lt
and le. For example, this matches activities scheduled during 2024:
GET /v1/CarePlan?activity-date=ge2024-01-01T00:00Z&activity-date=le2024-12-31T23:59Z