Publisert - 29.09.2026

API Endpoints

This API allows healthcare providers to create, update, and manage patient care plans. Each plan tracks clinical zones with recommended actions, maintains a full version history, supports optimistic concurrency control to prevent lost updates, and includes an activation, approval, and publication workflow.


Table of Contents


Environment

Development environment URL (no HelseID): https://planer.dev.nhn.no/

Swagger UI: https://planer.dev.nhn.no/api/swagger/

All requests and responses use Content-Type: application/json.


Authentication Channels

All endpoints are served on two channels, split by authentication type:

Channel Authentication Who
/api/personnel/... Personal HelseID login (DPoP token with personnel claims) Healthcare personnel acting through an EPJ system
/api/machine/... Machine HelseID token (no personal login) Machine-to-machine integrations acting on behalf of a personnel

Both channels expose the same endpoints — replace /api/personnel/ with /api/machine/ to use the machine channel. The machine channel does not require personnel claims in the token; instead the acting personnel is identified via the nhn-user-nin (required), nhn-user-name (required) and nhn-user-hpr (optional) request headers. See Authorization for details.


Activation

A plan has no status enum. Instead, activation is a pointer: active_version is the content version that can currently be shown to patients, or null if the plan has no active version.

  • New plans start with no active version.
  • POST $activate sets active_version to a specific content version.
  • POST $deactivate clears active_version — the plan should no longer be shown to patients.
  • PlanResponse.is_active is true only when the returned content version is the active one.
  • GET /api/personnel/plan/{id}/active returns the currently active version (404 if none).

Activation transitions and who made them are recorded in the version history via updated_by, updated_at, and comment. updated_by includes the acting personnel's NIN (nin), HPR number (hpr_number), and display name (display_name). The updated_by object identifies the acting personnel: nin (national identity number), hpr_number, and display_name.


Approval Lifecycle

Separate from activation, each plan tracks approval state per content version in the approvals array. Each entry has:

  • version — the content version the approval refers to
  • status — requested, given, or rejected
  • updated_at — when the state was last changed

Approval commands ($approval-requested, $approval-given, $approval-rejected) require both revision (for optimistic locking) and version (the content version being approved). A new content version starts with no approval entry; approving one version does not affect any other version.


Publication Lifecycle

Separate from activation and approval, each plan tracks per-provider publication state in the publication_status array. This allows different providers to independently publish or unpublish a version to their own patient-facing systems.

Each entry has:

  • provider_id — the provider that published or unpublished
  • version — the content version that was published
  • status — published or unpublished
  • updated_at — when the state was last changed

When a provider publishes a plan, they specify which content version to publish via $mark-published / $mark-unpublished. The array contains one entry per provider that has published or unpublished the plan.


How Versioning Works

Each plan has two version counters:

Revision (revision):

  • Starts at 1 when the plan is created.
  • Incremented by the server on every mutation.
  • Every mutation (except create) requires the current revision in the request body. If it doesn't match the server's revision, you'll get a 409 Conflict. This prevents lost updates when multiple users edit the same plan simultaneously.

Content version (version):

  • Starts at 1 when the plan is created.
  • Incremented only when clinical content is updated via $update-content.
  • Referenced by $activate, the approval commands, and $mark-published / $mark-unpublished to specify which content snapshot they apply to.

GET endpoints always return the latest content version. Old versions are immutable — once created, a version never changes. Retrieve any past version via GET /api/personnel/plan/{id}/version/{version}, which returns the historical content with current approvals, publication_status, and active_version merged in.


Endpoints

For request/response schemas, parameters, and status codes, use the Swagger UI. The paths below are shown with the /api/personnel/ channel prefix — the same paths are available under /api/machine/.

Method Path Description
POST /api/personnel/plan Create a new plan (starts with no active version)
GET /api/personnel/plan/{id}/$latest Latest version from write-side event store
GET /api/personnel/plan/{id} Latest version from read-side projection
GET /api/personnel/plan/{id}/active Currently active version (404 if none)
GET /api/personnel/plan/{id}/version/{version} Specific historical version
GET /api/personnel/plan/{id}/history Version history (audit trail)
GET /api/personnel/plan?patient={nin} Search plans by patient
POST /api/personnel/plan/{id}/$update-content Update clinical content
POST /api/personnel/plan/{id}/$activate Set a content version as the active version
POST /api/personnel/plan/{id}/$deactivate Clear the active version
POST /api/personnel/plan/{id}/$approval-requested Request approval for a content version
POST /api/personnel/plan/{id}/$approval-given Grant approval for a content version
POST /api/personnel/plan/{id}/$approval-rejected Reject approval for a content version
POST /api/personnel/plan/{id}/$mark-published Mark a content version as published for the requesting provider
POST /api/personnel/plan/{id}/$mark-unpublished Mark a content version as unpublished for the requesting provider

Search Filters

GET /api/personnel/plan requires patient (patient NIN) and supports the following optional filters:

Parameter Values Description
patient NIN Required. Only plans for this patient are returned
is_active true / false Filter by whether the plan has an active version
updated_since RFC3339 timestamp Only plans updated at or after this timestamp
published true / false Filter by publication state for the requesting provider. Requires the orgnr_parent header identifying the provider

Error Response

All error responses use this structure:

{
  "error": "plan not found",
  "code": "NOT_FOUND",
  "details": "extra context"
}
Field Type Description
error string Human-readable error message
code string Machine-readable error code (optional)
details any Optional extra context

Implementation Notes

This API uses CQRS (Command Query Responsibility Segregation) and event sourcing under the hood. The CQRS separation is internal; externally all endpoints are unified and served on both the /api/personnel/ and /api/machine/ channels. This provides scalability and consistency guarantees without exposing the CQRS pattern to API consumers.

Søk i Utviklerportalen

Søket er fullført!