English | Deutsch

Introduction

eauXchange is a dedicated server established for interaction with the GKVKomServer. It supports automatic and scheduled sending and receiving of data, keeps insurance company information up to date and tracks the public keys of the communication partners.

Two procedures are supported, each licensed separately:

EAU (/eau)

electronic certificates of incapacity for work (elektronische Arbeitsunfähigkeitsbescheinigung).

Vorerkrankung / LFZ (/pre)

preexisting condition enquiries for continued payment of wages (Entgeltersatzleistungen).

Every installation serves its own documentation: /help is the interactive API reference of the version you are running, /changelog lists the changes of all versions. Both work without a token.

Licensing

The license file is created by Javiis GmbH upon request. For this, the company number (BNR) (test or production) for which a license is issued is required. All requests are then sent for these company numbers. (For test mode, the test company number is used.)

The license also decides which procedures the installation may use (EAU, Vorerkrankung or both). Calls to a procedure that is not licensed are rejected.

Running the service

eauXchange is delivered as a Docker container and uses a PostgreSQL database to store all relevant information.

Place the following files into the config folder next to your docker-compose.yml:
  • application.yml - the server configuration (see below)

  • the license file provided by Javiis GmbH

  • the certificate and private key of your BNR (<bnr>.p7c / <bnr>.pkey), from which the keystore gkv.p12 is created

services:
  eauxchange:
    container_name: eauxchange_server
    image: www.eauxchange.de/eauxchange:latest
    ports:
      - "8085:8085"
    depends_on:
      - db
    environment:
      - SPRING_CONFIG_LOCATION=/opt/app/config/application.yml
      - DATABASE_CONNECTION=eauxchange_db
      - DATABASE_PORT=5432
    volumes:
      - ./config:/opt/app/config
  db:
    container_name: eauxchange_db
    image: postgres:14.12-bullseye
    restart: unless-stopped
    shm_size: 128mb
    environment:
      POSTGRES_DB: eauxchange
      POSTGRES_PASSWORD: <password>
      POSTGRES_USER: gkv_user
    volumes:
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - <EXTERNAL_PATH>:/var/lib/postgresql/data

The image is pulled from the registry www.eauxchange.de; the access data is provided together with the license. The database schema is created and migrated automatically on startup.

Server configuration

The configuration lives in application.yml in the config folder. The most important settings:

spring:
  datasource:
    username: gkv_user
    password: <password>
    url: jdbc:postgresql://eauxchange_db:5432/eauxchange

server:
  port: 8085

com:
  javiis:
    gkv:
      bnrTest: <test BNR>
      bnrProductive: <production BNR>
      # true = nothing is transmitted to the insurance companies (test/dry run)
      dryRunning: true
      # false = test certificate, true = production certificate
      use_production_certificate: false
      product:
        manufacturer: <company>
        name: <product name>
        version: <version>
        prodID: <product number>
        modID: <module number>
      contact:                       # technical contact, sent with every request
        name: Max Mustermann
        gender: M
        street: Musterstrasse
        streetNr: 74
        zip: 12345
        city: Berlin
        tel: 0123456789
        mail: kontakt@example.com
      keystore:
        test:
          path: /opt/app/config/test/gkv.p12
          password: <password>
          keyPassword: <password>
          alias: <BNR>
      receivePath: /opt/app/config/receive
      logSentFiles: true
      xmlLogPath: /opt/app/config/sent

Schedules

The background jobs run on cron expressions. The defaults are usually fine; all of them can be overridden:

Property Default Task

com.javiis.gkv.send.request.cron

0 0 1 * * *

Sends pending requests to the insurance companies

com.javiis.gkv.status.request.cron

0 30 * * * *

Asks the GKV server for new answers and downloads them

com.javiis.gkv.eau.import.cron

0 35 * * * *

Imports downloaded answer files

com.javiis.gkv.sync.request.cron

0 45 * * * *

Assigns answers that arrived without a usable reference to their request

com.javiis.gkv.certificate.expiry.cron

0 0 3 * * *

Checks the remaining validity of the communication certificates

Status requests must not run more often than every 15 minutes (restriction of the GKV communication server).

Notifications

Set com.javiis.gkv.mailSettings (host, port, sender, username, password, defaultRecipient) and com.javiis.gkv.sendEmailOnError: true to be informed about failures of the background jobs and about certificates that are about to expire.

Authentication

All API calls require an API token; only the built-in help (/help, /api-docs) and the release notes (/changelog) are open. A token is a JWT that is valid for the company numbers it was issued for and carries an expiry date.

Creating the first token
  1. On the very first start the server creates the user root and writes its generated password once into the log. Note it down; it is not shown again.

  2. Create an API token with that user (HTTP basic authentication):

    curl -u root:<password from the log> \
         -X POST 'https://<host>/security/createAPIToken?expiryDate=2027-12-31T23:59:59&note=ERP' \
         -H 'Content-Type: application/json' \
         -d '["<BNR>"]'
  3. Use the returned token for every further call:

    curl -X POST 'https://<host>/eau/getOpenEauResponses' \
         -H 'Authorization: Bearer <token>'

Tokens can be listed, disabled, enabled and deleted (/security/getTokens, /security/disableTokens, /security/enableTokens, /security/deleteTokens).

A request whose BNR is not covered by the token is rejected with HTTP 403. When you add a new company number, issue a new token for it - otherwise those requests stay unsent.

How to use the service

For a detailed list of API calls and schemas visit the built-in help of your installation under /help. It documents exactly the version you are running.

Creating an eAU request

In the built-in help: createEauRequests

To schedule an eAU request, an object of EauRequest type is sent. The startDate field refers to the point in time when the absence was taken. If the field contactPerson is empty, the default contact person set at creation time will be used. The response will be an errorCode per request sent (NONE if the request is scheduled successfully).

operation::createEauRequests[snippets='http-request,http-response']

Retrieving an eAU response

In the built-in help: getOpenEauResponses

You can retrieve open eAU responses with the method getOpenEauResponses. All eAU responses that are not marked as checked are retrieved.

operation::getOpenEauResponses[snippets='http-request,http-response']

Marking an eAU response as finished

In the built-in help: markEauResponsesAsChecked

In order to avoid data loss, all eAU responses must be marked as completed once they are retrieved from the server. This status update is achieved by invoking the markEauResponsesAsChecked method.

operation::test-mark-eau-responses-as-checked[snippets='http-request,http-response']

Schedule eAU cancellation

In the built-in help: stornoEauRequest

You can abort an eAU request by invoking the stornoEauRequest method. eAU requests that have not yet been transmitted will be obliterated instantly, while requests that have already been dispatched will be queued for cancellation during the next synchronization process.

operation::stornoEauRequest[snippets='http-request,http-response']

Resending requests

scheduleEauRequestsForResend queues requests again, for example after an insurance company asked for a repetition. scheduleEauRequestsForResendWithContactPerson does the same but replaces the technical contact, which is the right call after the contact person has changed.

Searching requests and responses

getOpenEauRequests returns everything that is still waiting for an answer. findEauRequestByCriteria and findEauResponseByCriteria search by freely combinable criteria (name, insurance number, period, state). getEauRequest and getEauResponseFromRequestID fetch a single record.

If a request stays open although the answer has already arrived, syncResponsesWithRequests assigns pending answers to their requests. The same runs automatically (see the schedules above).

Execute sending/retrieving job manually

In the built-in help: manualexecuteRequestSending

You have the option to initiate a manual send or retrieve task by invoking the methods manualExecuteRequestSending and manualExecuteStatusRequest in /common. Typically, these methods are used for testing or when an answer is needed before the next scheduled run.

operation::manualExecuteStatusRequest[snippets='http-request']

Preexisting condition enquiries (Vorerkrankung / LFZ)

Requires the Vorerkrankung license. The flow mirrors the eAU procedure and uses the /pre endpoints.

  1. createPreexistingConditionInfoRequests schedules the enquiries. Duplicates - same person, same period - are rejected right away with an explaining error instead of being sent twice.

  2. getOpenPreexistingConditionInfoRequests lists everything that is still waiting for an answer.

  3. getOpenPreexistingConditionInfoResponses returns the answers that have not been marked as checked. The optional parameter limit restricts the number of records returned.

  4. markResponseAsChecked confirms the processing, exactly like in the eAU procedure.

getPreexistingConditionInfoRequest, getPreexistingConditionInfoResponse and findPreexistingConditionInfoRequestsByAbAGDate fetch single records or search by the date of the employer enquiry.

Understanding an answer

For every enquiry the data delivered by the insurance company is available in raw and in parsed form: getRawRequest, getRawResponse, getRawResponseByRequest, getRawErrorResponse and the corresponding getParsed…​ methods. This is the fastest way to clarify a rejection without a support request - the rejection reason of the insurance company is part of the answer.

Validation service

The server provides methods for validating insurance company numbers and employee insurance numbers according to the standard validation rules (/validation/companyBnr, /validation/insuranceNr). More information: In the built-in help: validateInsuranceNr

Insurance companies

The insurance company data is ordinarily synchronized automatically, however, you also have the opportunity to manually trigger this synchronization by calling the triggerInsuranceCompanyRefresh method in /insuranceCompany. This action updates all insurance companies and their public keys, in addition to their associations with their respective DAVs. findInsuranceCompanyByBnr, findInsuranceCompanyByCriteria and getAllValidInsuranceCompanies read the data.

Operating notes

Which version am I running?

/changelog shows the version of the installation and the changes of all versions; /changelog.json delivers the same information in machine-readable form.

An answer file could not be imported

Files that could not be processed are kept. reimportFailedResponses processes them again after the cause has been fixed; manualTriggerResponseImports triggers the import of everything that has been downloaded.

Nothing is transmitted

Check dryRunning (must be false in production), the certificate selection (use_production_certificate) and whether the API token covers the company number of the requests.

Certificates

The remaining validity is checked daily. With the mail settings configured, a warning is sent early enough to exchange the certificate before the transmission stops.

Looking at the file sequence number

Use POST /dav/getCurrentDAVFileCounter - it only reads. The returned value is the number of the file that was sent last. Every DAV keeps one counter per procedure: the optional parameter procedure selects it, EAA (default) for eAU and EEL for preexisting condition enquiries. The procedure’s module has to be licensed - EAA requires EAU, EEL requires VORERKRANKUNG - otherwise the call is answered with 403.

POST /dav/drawNextDAVFileCounter draws the next number and consumes it. The number cannot be given back, and none of your files will carry it. Use it only in recovery situations, never to look at a value. The older path getCurrentFileCounterForBnr does the same thing and still works, but is deprecated - its name reads like a query although the call changes data.