Guide
  • SDK

    • PHP
    • .NET
  • Postman

    • Postman Collection
Api Docs
Guide
  • SDK

    • PHP
    • .NET
  • Postman

    • Postman Collection
Api Docs
  • Getting Started

    • Getting Started
    • Integration process
  • Tools and libraries

    • SDK for PHP
    • SDK for .NET
    • Postman Collection
  • Fundamentals

    • Authorization
    • Errors
    • Extensions
    • Rate Limits
  • API Objects

    • Resources
    • Managing calendars
    • Online Features
    • Patient Presence
    • NFZ public healthcare (Poland)
  • Callbacks

    • Push vs Pull
    • Real-time requests
  • Mappings

    • Vendor mapping
  • Changelog

    • Changelog
DOCPLANNER INTEGRATIONS

NFZ public healthcare (Poland)

How to enable bookings to public healthcare (NFZ) in Poland through the existing Insurances, Slots, and Bookings endpoints.
Docplanner integrations - img
Docplanner integrations - icon

Public Healthcare bookings support is currently limited only to Poland, activation needs to be coordinated with Docplanner team.

Docplanner integrations - icon

Guide explains practical implementation details on how to feed the doctor profile with NFZ-related attributes (insurances, slots).

Docplanner integrations - img

Public healthcare bookings in Poland are funded by NFZ (Narodowy Fundusz Zdrowia) and are increasingly expected by patients searching for a doctor on znanylekarz.pl. This guide walks integrators through the small set of API additions required to expose NFZ availability alongside private slots without restructuring an existing integration.

Context

NFZ (Narodowy Fundusz Zdrowia) is the public healthcare provider in Poland. It is exposed through the existing Insurances, Slots, and Bookings endpoints as insurance provider id 13 on znanylekarz.pl. NFZ uses the same address_services as private bookings — the only differentiator is per-work-period insurance metadata on PUT /slots. NFZ does not use insurance plans, so insurance_plans and insurance_plan_id should be omitted from all NFZ payloads.

Enabling NFZ requires four small additions to an existing integration:

  • Make sure the address has insurance support enabled.
  • Link NFZ as an insurance provider on the address.
  • Feed NFZ slots (alone, or mixed with private in the same request).
  • Read the insurance object on bookings.

Coordinate activation with Docplanner

Before turning NFZ flows on in production, coordinate with the Docplanner team (integrations@docplanner.com). Activation granularity is being finalized — code-only enablement is not enough.

Dictionary lookup

NFZ is insurance provider id 13 on znanylekarz.pl and stable across staging and production. We still recommend a one-time call to getInsuranceProviders at integration setup to sanity-check the dictionary against your environment:

GET https://www.{domain}/api/v3/integration/insurance-providers

The response contains an entry like { "insurance_provider_id": "13", "name": "NFZ" }.

Address insurance support

For NFZ slots to be displayed on the doctor's profile, the address itself must have insurance support enabled. Read the current value with getAddress using the address.insurance_support scope:

GET https://www.{domain}/api/v3/integration/facilities/{facility_id}/doctors/{doctor_id}/addresses/{address_id}?with=address.insurance_support

If the address is set to "private", update it via updateAddress:

PATCH https://www.{domain}/api/v3/integration/facilities/{facility_id}/doctors/{doctor_id}/addresses/{address_id}
{
  "insurance_support": "private_and_insurance"
}

The insurance_support field accepts "private", "insurance", or "private_and_insurance". For Polish practices that offer both privately paid and NFZ-funded visits, "private_and_insurance" is the recommended default — it exposes both flows and does not require migrating existing private slots.

Linking NFZ to the doctor's address

Once the address accepts insurance, list the linked providers and check whether NFZ is already there:

GET https://www.{domain}/api/v3/integration/facilities/{facility_id}/doctors/{doctor_id}/addresses/{address_id}/insurance-providers

If insurance_provider_id: "13" is missing, add it with addAddressInsuranceProvider:

POST https://www.{domain}/api/v3/integration/facilities/{facility_id}/doctors/{doctor_id}/addresses/{address_id}/insurance-providers
{
  "insurance_provider_id": "13"
}

Use deleteAddressInsuranceProvider to remove NFZ when a doctor stops accepting public healthcare on that address. If NFZ is added on a doctor or address that is not contracted with NFZ on the Docplanner side, the API returns a validation error — surface it to clinic staff so the partner can confirm with Docplanner whether the doctor should be NFZ-eligible.

After linking, the address shows the Przyjmuje na NFZ label on the doctor profile:

Feeding NFZ slots

NFZ availability is created through the same replaceSlots endpoint you already use for private slots. The differentiator is the per-work-period insurance metadata:

  • insurance_accepted — "with-insurance-only", "with-and-without-insurance", or "without-insurance-only" (the latter is also the default when omitted).
  • insurance_providers — array of provider ids. For NFZ this is [13].

Insurance config applies to every service in a work period

Each element of the slots[] array is one work period with a single start/end and a list of address_services. The insurance_accepted and insurance_providers you set on that work period apply to all services listed inside it. To use different insurance rules for different services, place them in separate work periods.

A pure NFZ block:

{
  "slots": [
    {
      "address_services": [
        { "address_service_id": "111", "duration": 30 },
        { "address_service_id": "112", "duration": 15 }
      ],
      "start": "2026-06-01T08:00:00+02:00",
      "end":   "2026-06-01T12:00:00+02:00",
      "insurance_accepted": "with-insurance-only",
      "insurance_providers": [13]
    }
  ]
}

Private and NFZ in the same request — send them as separate work periods, even on the same day:

{
  "slots": [
    {
      "address_services": [
        { "address_service_id": "111", "duration": 30 },
        { "address_service_id": "112", "duration": 15 }
      ],
      "start": "2026-06-01T08:00:00+02:00",
      "end":   "2026-06-01T12:00:00+02:00",
      "insurance_accepted": "with-insurance-only",
      "insurance_providers": [13]
    },
    {
      "address_services": [
        { "address_service_id": "111", "duration": 30 }
      ],
      "start": "2026-06-01T13:00:00+02:00",
      "end":   "2026-06-01T17:00:00+02:00",
      "insurance_accepted": "without-insurance-only"
    }
  ]
}

If a single period should be bookable as either NFZ or private, use "with-and-without-insurance" together with insurance_providers: [13]. NFZ visits are free for the patient, so on the address service both price: 0 and price: null are acceptable — pick whichever matches your existing convention.

Common 4xx responses on PUT /slots:

  • Insurance providers missing when with-insurance-only — you forgot insurance_providers.
  • Insurance providers not supported when without-insurance-only — drop the providers array.
  • Insurance provider does not exist — the id is unknown for the marketplace.
  • 429 Too Many Requests — duplicate request for the same address/payload while the previous one is still processing.

NFZ on bookings

Booking from the clinic system

When the partner system books an NFZ visit, call bookSlot and include insurance_provider_id: "13". Do not send insurance_plan_id.

POST https://www.{domain}/api/v3/integration/facilities/{facility_id}/doctors/{doctor_id}/addresses/{address_id}/slots/{start}/book
{
  "address_service_id": "111",
  "duration": 30,
  "is_returning": false,
  "insurance_provider_id": "13",
  "patient": {
    "name": "Anna",
    "surname": "Kowalska",
    "email": "anna.kowalska@example.com",
    "phone": "+48123123123"
  }
}

Reading insurance on existing bookings

getBookings and getBooking include an insurance object on each booking. Detect NFZ via insurance.id === "13":

{
  "id": "2134124",
  "status": "booked",
  "start_at": "2026-06-01T09:00:00+02:00",
  "end_at":   "2026-06-01T09:30:00+02:00",
  "duration": 30,
  "patient": { "name": "Anna", "surname": "Kowalska" },
  "insurance": {
    "id": "13",
    "name": "NFZ",
    "plan": null,
    "plan_id": null
  }
}

Bookings created by patients on Docplanner

Once insurance_support is enabled on the address, the existing booking callbacks (slot-booking, slot-booked, booking-moved, booking-canceled) are automatically enriched with an insurance object inside visit_booking / visit_booking_request. Read it directly from the payload — there is no need to re-fetch the booking via GET to determine the insurance provider. There is no separate "enable enriched callbacks" toggle; the address-level insurance_support setting is the only required configuration.

{
  "name": "slot-booked",
  "data": {
    "facility": { "id": "1", "name": "Sample hospital" },
    "doctor":   { "id": "123", "name": "Anna", "surname": "Lekarz" },
    "address":  { "id": "111", "name": "Example clinic" },
    "visit_booking": {
      "id": "6263715",
      "status": "booked",
      "insurance": { "id": "13", "name": "NFZ", "plan": null, "plan_id": null }
    }
  },
  "created_at": "2026-06-01T08:38:32+02:00"
}

For private bookings the insurance object is null; for NFZ bookings insurance.id === "13".

Moving, cancelling, filtering

The insurance on a booking is fixed at creation. moveBooking preserves it — moving to a new time, service, or address does not change insurance.id. To switch a patient between private and NFZ, cancel the booking and create a new one on a slot with the desired insurance configuration.

There is no insurance_provider_id filter on GET /slots or GET /bookings. Filter on the partner side: inspect insurance.id per item from GET /bookings, and keep your own NFZ-vs-private flag for slots based on what you submitted to replaceSlots (GET /slots does not echo the insurance metadata back).

Marketplace presentation

On znanylekarz.pl, NFZ availability is exposed to patients alongside private slots once the steps above are in place. Patients see a dedicated NFZ filter and badge on doctor profiles and search listings, and the booking flow does not ask the patient for additional NFZ-specific data — no PESEL or insurance card prompts at the API layer. UI details may evolve, so treat them as informational; the contract between partner and Docplanner remains the API behaviour described above.

Testing

NFZ flows can be exercised in your regular partner test suite. NFZ enablement is a Docplanner-side configuration, so coordinate test-environment setup with the Docplanner team before running end-to-end NFZ scenarios.

Implementation checklist

  • Verify insurance_provider_id for NFZ is 13 on znanylekarz.pl (one-time lookup via GET /insurance-providers).
  • For each NFZ-eligible address, set insurance_support to "private_and_insurance" via PATCH .../addresses/{address_id}.
  • For each NFZ-eligible address, ensure insurance_provider_id: "13" is linked via POST .../insurance-providers.
  • In replaceSlots, send insurance_accepted and insurance_providers: [13] on NFZ work periods. Keep private periods either with "without-insurance-only" or with no insurance fields.
  • On outgoing bookSlot, include insurance_provider_id: "13" for NFZ visits. Do not send insurance_plan_id.
  • On incoming bookings (both GET and callback notifications), read the insurance object directly from the payload and treat insurance.id === "13" as NFZ.
  • Coordinate activation and test-environment setup with the Docplanner team.
Prev
Patient Presence