diff --git a/README.adoc b/README.adoc
index 056d3706dd0cf96aed81fa2aac5bbc6acee38cb3..d109fc6bfb0878efbb038c69b784e4bebb8cb12e 100644
--- a/README.adoc
+++ b/README.adoc
@@ -28,6 +28,7 @@ We appreciate your help in improving the project!
 - link:docs/sharing-code.adoc[Sharing Code]
 - link:reverse-proxy/README.md[Reverse Proxy]
 - link:docs/content-security-policy-header.adoc[Content Security Policy (CSP) Header]
+- link:docs/flaky-tests.adoc[Flaky Tests]
 
 == Licensing
 
diff --git a/admin-portal/src/lib/components/view/actors/ActorTable.tsx b/admin-portal/src/lib/components/view/actors/ActorTable.tsx
index 046efe82e032b30f196673fbfc7e3a5e38f8b8a9..14d62287e782a0b9ec08d57e68b33bf8dc3384c7 100644
--- a/admin-portal/src/lib/components/view/actors/ActorTable.tsx
+++ b/admin-portal/src/lib/components/view/actors/ActorTable.tsx
@@ -12,7 +12,6 @@ import {
   ApiAdminStagedEntityAdminPartialActor,
   ApiAdminStagedEntityType,
   ApiGetOrgUnitsResponse,
-  ApiStagingStatus,
 } from "@eshg/admin-portal-api/serviceDirectory";
 import { createColumnHelper, filterFns } from "@tanstack/react-table";
 import { useMemo } from "react";
@@ -337,7 +336,7 @@ function useGetSubRows() {
             _matchingClientRules: [],
             _matchingServerRules: [],
             stagedEntityType: ApiAdminStagedEntityType.Del,
-            stagingStatus: ApiStagingStatus.WorkInProgress,
+            stagingStatus: sa.stagingStatus,
             _type: "actor",
             _parent: originalRow,
           };
diff --git a/admin-portal/src/lib/components/view/org-units/OrgUnitTable.tsx b/admin-portal/src/lib/components/view/org-units/OrgUnitTable.tsx
index 080a4bf27953a25c4fb917604452ee6af1c01162..1f53c756df09d9c237491485d7cf2ca1b657cb54 100644
--- a/admin-portal/src/lib/components/view/org-units/OrgUnitTable.tsx
+++ b/admin-portal/src/lib/components/view/org-units/OrgUnitTable.tsx
@@ -12,7 +12,6 @@ import {
   ApiFederalState,
   ApiGetOrgUnitsResponse,
   ApiOrgUnitType,
-  ApiStagingStatus,
 } from "@eshg/admin-portal-api/serviceDirectory";
 import { createColumnHelper, filterFns } from "@tanstack/react-table";
 import { useMemo } from "react";
@@ -206,7 +205,7 @@ function getSubRows(orgUnits: ApiGetOrgUnitsResponse | undefined) {
             author: sou.author,
             _override: DeleteRow,
             stagedEntityType: ApiAdminStagedEntityType.Del,
-            stagingStatus: ApiStagingStatus.WorkInProgress,
+            stagingStatus: sou.stagingStatus,
             _type: "orgUnit",
             _parent: originalRow,
           },
diff --git a/admin-portal/src/lib/components/view/rules/RuleTable.tsx b/admin-portal/src/lib/components/view/rules/RuleTable.tsx
index 3399c16500ca123ae1b7d56a7bdfb921ff88d2b8..708afcc17a15c4ada1f6488addd2878d46defe5c 100644
--- a/admin-portal/src/lib/components/view/rules/RuleTable.tsx
+++ b/admin-portal/src/lib/components/view/rules/RuleTable.tsx
@@ -9,7 +9,6 @@ import {
   ApiAdminStagedEntityAdminPartialRule,
   ApiAdminStagedEntityType,
   ApiGetRulesResponse,
-  ApiStagingStatus,
 } from "@eshg/admin-portal-api/serviceDirectory";
 import { createColumnHelper, filterFns } from "@tanstack/react-table";
 import { useMemo } from "react";
@@ -216,7 +215,7 @@ function useGetSubRows() {
             _matchingClientActors: [],
             _matchingServerActors: [],
             stagedEntityType: ApiAdminStagedEntityType.Del,
-            stagingStatus: ApiStagingStatus.WorkInProgress,
+            stagingStatus: sr.stagingStatus,
             _type: "rule",
             _parent: originalRow,
           };
diff --git a/backend/auditlog-api/src/main/java/de/eshg/auditlog/GetEncryptedSymmetricKeyResponse.java b/backend/auditlog-api/src/main/java/de/eshg/auditlog/GetEncryptedSymmetricKeyResponse.java
index 7a54ad387e626179c59d0396feb89c5f5d905cb4..87434eed6d950e44f13b7e90edb57b44824bfbb8 100644
--- a/backend/auditlog-api/src/main/java/de/eshg/auditlog/GetEncryptedSymmetricKeyResponse.java
+++ b/backend/auditlog-api/src/main/java/de/eshg/auditlog/GetEncryptedSymmetricKeyResponse.java
@@ -5,7 +5,10 @@
 
 package de.eshg.auditlog;
 
+import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import java.util.List;
 
 public record GetEncryptedSymmetricKeyResponse(
-    @NotNull byte[] encapsulatedKey, @NotNull byte[] encryptedSymmetricKey) {}
+    @NotNull @NotEmpty List<Byte> encapsulatedKey,
+    @NotNull @NotEmpty List<Byte> encryptedSymmetricKey) {}
diff --git a/backend/auditlog/openApi.yaml b/backend/auditlog/openApi.yaml
index fcc47e4011e8123cc0cf1c958b3f4871e4e52a25..c2f94ee0a8c95d059fbd28105feab2699dfcd60b 100644
--- a/backend/auditlog/openApi.yaml
+++ b/backend/auditlog/openApi.yaml
@@ -539,11 +539,15 @@ components:
       type: object
       properties:
         encapsulatedKey:
-          type: string
-          format: byte
+          type: array
+          items:
+            type: string
+            format: byte
         encryptedSymmetricKey:
-          type: string
-          format: byte
+          type: array
+          items:
+            type: string
+            format: byte
       required:
       - encapsulatedKey
       - encryptedSymmetricKey
diff --git a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
index 1339f44bc802ccae80eef80383235c423bf950dd..b41055de36e083e2630ce3cbd75f7a21c57ae93a 100644
--- a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
+++ b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
@@ -55,6 +55,7 @@ import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.Collections;
 import java.util.Comparator;
@@ -68,6 +69,7 @@ import java.util.UUID;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.apache.commons.lang3.ArrayUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.ContentDisposition;
@@ -337,12 +339,17 @@ public class AuditLogController implements AuditLogApi, AuditLogArchivingApi {
       byte[] encapsulatedKey = Files.readAllBytes(encapsulatedKeyPath);
       log.info("Using encapsulated key {}", encapsulatedKeyPath);
 
-      return new GetEncryptedSymmetricKeyResponse(encapsulatedKey, encryptedSymmetricKey);
+      return new GetEncryptedSymmetricKeyResponse(
+          toByteList(encapsulatedKey), toByteList(encryptedSymmetricKey));
     } catch (IOException e) {
       throw new UncheckedIOException("Unable to read user specific key", e);
     }
   }
 
+  private List<Byte> toByteList(byte[] byteArray) {
+    return Arrays.asList(ArrayUtils.toObject(byteArray));
+  }
+
   @Override
   public GetAvailableAuditLogsResponse getAvailableLogs(
       GetAvailableAuditLogsFilterOptions filterOptions,
diff --git a/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java b/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java
index 831b82d30456b4dedba46d6acd4f2530043caa5e..9383062bb0b087eca1f325e6d2c41afabb872a58 100644
--- a/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java
+++ b/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java
@@ -13,6 +13,8 @@ import org.springframework.util.AntPathMatcher;
 import org.springframework.web.util.UriComponentsBuilder;
 
 public abstract class LoginMethod {
+  public static final String LOGIN_LOCALE_PARAM_NAME = "ui_locales";
+
   protected final AntPathMatcher antPathMatcher = new AntPathMatcher();
   protected final AuthProperties authProperties;
 
@@ -28,6 +30,14 @@ public abstract class LoginMethod {
       OAuth2AuthorizationRequest auth2AuthorizationRequest, String redirectUrl) {
     return OAuth2AuthorizationRequest.from(auth2AuthorizationRequest)
         .additionalParameters(params -> this.applyParameters(params, redirectUrl))
+        .additionalParameters(
+            params -> {
+              String urlPath = UriComponentsBuilder.fromUriString(redirectUrl).build().getPath();
+              LocaleAndPath localeAndPath = extractLanguagePathPrefix(urlPath);
+              if (localeAndPath.language() != null) {
+                params.put(LOGIN_LOCALE_PARAM_NAME, localeAndPath.language());
+              }
+            })
         .build();
   }
 
@@ -37,21 +47,26 @@ public abstract class LoginMethod {
       return false;
     }
     String urlPath = UriComponentsBuilder.fromUriString(url).build().getPath();
-    String normalizedUrlPath = replaceLanguagePathPrefix(urlPath);
+    LocaleAndPath localeAndPath = extractLanguagePathPrefix(urlPath);
+    String normalizedUrlPath = localeAndPath.path();
     return normalizedUrlPath != null
         && patterns.stream().anyMatch(pattern -> antPathMatcher.match(pattern, normalizedUrlPath));
   }
 
-  private String replaceLanguagePathPrefix(String url) {
+  private LocaleAndPath extractLanguagePathPrefix(String url) {
     List<String> languagePathPrefixes = authProperties.getLanguagePathPrefixes();
     if (languagePathPrefixes == null) {
-      return url;
+      return new LocaleAndPath(null, url);
     }
     for (String languagePathPrefix : languagePathPrefixes) {
       if (url.startsWith(languagePathPrefix + "/")) {
-        return url.substring(languagePathPrefix.length());
+        String locale = url.substring(1, languagePathPrefix.length());
+        String subPath = url.substring(languagePathPrefix.length());
+        return new LocaleAndPath(locale, subPath);
       }
     }
-    return url;
+    return new LocaleAndPath(null, url);
   }
+
+  record LocaleAndPath(String language, String path) {}
 }
diff --git a/backend/auth/src/main/resources/application.properties b/backend/auth/src/main/resources/application.properties
index defc4ab54b07cf9f576ec3545cf61726cee25b25..e9fdaf577081a00c8ce1b206e19361e90369ac9e 100644
--- a/backend/auth/src/main/resources/application.properties
+++ b/backend/auth/src/main/resources/application.properties
@@ -1,5 +1,3 @@
-spring.profiles.active=employee-portal
-
 server.port=8092
 
 spring.security.oauth2.client.registration.keycloak.client-id=system-eshg-auth-service
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/PersonApi.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/PersonApi.java
index 51e15c665fae2592529cc142704b3d18298005a5..1350fa1c0205cb202babfdc277f399814f98171a 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/PersonApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/PersonApi.java
@@ -120,6 +120,17 @@ public interface PersonApi {
           @Valid
           AddPersonFileStatesRequest request);
 
+  @PostExchange(FILE_STATES_URL + "/bulk-search")
+  @ApiResponse(responseCode = "200")
+  @Operation(
+      summary =
+          """
+    Search multiple reference persons by the given key attributes
+    and return all file state ids associated with these reference persons.
+    """)
+  GetPersonFileStateIdsByKeyAttributesResponse getPersonFileStateIdsByReferencePersonKeyAttributes(
+      @Valid @RequestBody GetPersonFileStateIdsByKeyAttributesRequest request);
+
   @PostExchange(FILE_STATES_URL + "/bulk-get")
   @ApiResponse(responseCode = "200")
   @Operation(summary = "Get multiple persons")
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c879f6aa4d3f7ff08e5d8392576a72b19d3a1d8
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesRequest.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.centralfile.api.person;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.Set;
+
+public record GetPersonFileStateIdsByKeyAttributesRequest(
+    @Valid @NotEmpty Set<PersonKeyAttributes> searchAttributes) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..9271a700793ba4337317c5db84b651c5d0c8aa25
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateIdsByKeyAttributesResponse.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.centralfile.api.person;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public record GetPersonFileStateIdsByKeyAttributesResponse(
+    @Valid @NotNull Map<PersonKeyAttributes, List<UUID>> fileStateIdsByPersons) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonKeyAttributes.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonKeyAttributes.java
new file mode 100644
index 0000000000000000000000000000000000000000..debb4ae35b5ee80d517bc26074a165856dc0ef2c
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonKeyAttributes.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.centralfile.api.person;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.springframework.util.Assert;
+
+public record PersonKeyAttributes(
+    @NotBlank String firstName, @NotBlank String lastName, @NotNull LocalDate dateOfBirth) {
+
+  public static final Pattern SERIALIZATION_PATTERN =
+      Pattern.compile(
+          "PersonKeyAttributes\\[firstName=(.+?), lastName=(.+?), dateOfBirth=(.+?)\\]");
+
+  /*
+  Required for deserializing PersonKeyAttributes as Map keys
+  See de.eshg.base.centralfile.api.person.GetPersonFileStateIdsByKeyAttributesResponse
+   */
+  @JsonCreator
+  private static PersonKeyAttributes fromJsonString(String s) {
+    Matcher matcher = SERIALIZATION_PATTERN.matcher(s);
+    Assert.isTrue(matcher.matches(), "Did not match expected pattern");
+    Assert.isTrue(matcher.groupCount() == 3, "Wrong number of groups");
+    return new PersonKeyAttributes(
+        matcher.group(1), matcher.group(2), LocalDate.parse(matcher.group(3)));
+  }
+}
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
index 9d35cd41079317e2b71092a0fd5800084b41f79e..370462f9351c7e8ab2ee87f6e023d9e9840772cf 100644
--- a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
@@ -7,8 +7,10 @@ package de.eshg.base.gdpr;
 
 import de.eshg.api.commons.InlineParameterObject;
 import de.eshg.base.gdpr.api.AddGdprProcedureRequest;
+import de.eshg.base.gdpr.api.GdprProcedureChangeStatusRequest;
 import de.eshg.base.gdpr.api.GetGdprProcedureDetailsPageResponse;
 import de.eshg.base.gdpr.api.GetGdprProcedureResponse;
+import de.eshg.base.gdpr.api.SetMatterOfConcernRequest;
 import de.eshg.rest.service.security.config.BaseUrls;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.service.annotation.GetExchange;
 import org.springframework.web.service.annotation.HttpExchange;
 import org.springframework.web.service.annotation.PostExchange;
+import org.springframework.web.service.annotation.PutExchange;
 
 @HttpExchange(url = GdprProcedureApi.BASE_URL)
 public interface GdprProcedureApi {
@@ -59,4 +62,18 @@ public interface GdprProcedureApi {
   GetGdprProcedureResponse addCentralFileIdToGdprProcedure(
       @Parameter(description = "The Id of the GDPR procedure.") @PathVariable("id") UUID id,
       @RequestBody @Valid AddCentralFileIdToGdprProcedureRequest request);
+
+  @PutExchange("/{id}/matter-of-concern")
+  @ApiResponse(responseCode = "200")
+  @Operation(
+      summary =
+          "Changes the matter of concern of this GDPR procedure, this is only relevant for right to correction and right to objection.")
+  void setMatterOfConcern(
+      @PathVariable("id") UUID id, @RequestBody @Valid SetMatterOfConcernRequest request);
+
+  @PostExchange("/{id}/change-status")
+  @ApiResponse(responseCode = "200")
+  @Operation(summary = "Changes the current status of the GDPR procedure.")
+  void changeStatus(
+      @PathVariable("id") UUID id, @RequestBody @Valid GdprProcedureChangeStatusRequest request);
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureChangeStatusRequest.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureChangeStatusRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8b6bdaafc8fddb6c2ae4464760307eb5ea221d4
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureChangeStatusRequest.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.gdpr.api;
+
+import jakarta.validation.constraints.NotNull;
+
+public record GdprProcedureChangeStatusRequest(
+    @NotNull GdprProcedureStatusDto newStatus, @NotNull long version) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GetGdprProcedureResponse.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GetGdprProcedureResponse.java
index f59383438883995577dffa01b3d1d021060842de..6437833429539a79e04edd2a699ce81edc0fa233 100644
--- a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GetGdprProcedureResponse.java
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GetGdprProcedureResponse.java
@@ -34,4 +34,9 @@ public record GetGdprProcedureResponse(
             description = "The date and time of when the GDPR procedure was created.",
             example = "2024-02-01T00:00:00.123456Z")
         @NotNull
-        Instant createdAt) {}
+        Instant createdAt,
+    @Schema(
+            description =
+                "The matter of concern for this GDPR procedure, only relevant for right to correction and right to objection.",
+            example = "Person requested to stop all related procedures.")
+        String matterOfConcern) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/SetMatterOfConcernRequest.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/SetMatterOfConcernRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc26c898ff1ddcb2b84bc2d994ed8a7e6472d605
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/SetMatterOfConcernRequest.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.gdpr.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+public record SetMatterOfConcernRequest(
+    @Schema(description = "The matter of concern for the GDPR procedure.") @NotBlank String concern,
+    @NotNull long version) {}
diff --git a/backend/base/openApi.yaml b/backend/base/openApi.yaml
index 3412a8b50131b9091faa3417ac405cc0eb42f8a1..9935c0f4427f9825d7c4a044facf6f964e17f366 100644
--- a/backend/base/openApi.yaml
+++ b/backend/base/openApi.yaml
@@ -1326,6 +1326,28 @@ paths:
       summary: Add central file id to GDPR procedure.
       tags:
       - GdprProcedure
+  /gdpr-procedures/{id}/change-status:
+    post:
+      operationId: changeStatus
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/GdprProcedureChangeStatusRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Changes the current status of the GDPR procedure.
+      tags:
+      - GdprProcedure
   /gdpr-procedures/{id}/details-page:
     get:
       operationId: getGdprProcedureDetailsPage
@@ -1348,6 +1370,29 @@ paths:
         frontend to display the GDPR Procedure.
       tags:
       - GdprProcedure
+  /gdpr-procedures/{id}/matter-of-concern:
+    put:
+      operationId: setMatterOfConcern
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/SetMatterOfConcernRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: "Changes the matter of concern of this GDPR procedure, this is only\
+        \ relevant for right to correction and right to objection."
+      tags:
+      - GdprProcedure
   /inventoryItems:
     get:
       operationId: getInventoryItems
@@ -1926,6 +1971,27 @@ paths:
       summary: Get multiple persons
       tags:
       - Person
+  /persons/centralfilestates/bulk-search:
+    post:
+      operationId: getPersonFileStateIdsByReferencePersonKeyAttributes
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/GetPersonFileStateIdsByKeyAttributesRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetPersonFileStateIdsByKeyAttributesResponse"
+          description: OK
+      summary: |
+        Search multiple reference persons by the given key attributes
+        and return all file state ids associated with these reference persons.
+      tags:
+      - Person
   /persons/centralfilestates/external-source:
     post:
       operationId: addPersonFromExternalSource
@@ -5241,6 +5307,17 @@ components:
       - dateOfBirth
       - firstName
       - lastName
+    GdprProcedureChangeStatusRequest:
+      type: object
+      properties:
+        newStatus:
+          $ref: "#/components/schemas/GdprProcedureStatus"
+        version:
+          type: integer
+          format: int64
+      required:
+      - newStatus
+      - version
     GdprProcedureSortKey:
       type: string
       enum:
@@ -5800,6 +5877,11 @@ components:
           oneOf:
           - $ref: "#/components/schemas/GdprFacility"
           - $ref: "#/components/schemas/GdprPerson"
+        matterOfConcern:
+          type: string
+          description: "The matter of concern for this GDPR procedure, only relevant\
+            \ for right to correction and right to objection."
+          example: Person requested to stop all related procedures.
         status:
           $ref: "#/components/schemas/GdprProcedureStatus"
         type:
@@ -5892,6 +5974,28 @@ components:
       - contactAddressDiff
       - personDetailsDiff
       - referenceVersion
+    GetPersonFileStateIdsByKeyAttributesRequest:
+      type: object
+      properties:
+        searchAttributes:
+          type: array
+          items:
+            $ref: "#/components/schemas/PersonKeyAttributes"
+          uniqueItems: true
+      required:
+      - searchAttributes
+    GetPersonFileStateIdsByKeyAttributesResponse:
+      type: object
+      properties:
+        fileStateIdsByPersons:
+          type: object
+          additionalProperties:
+            type: array
+            items:
+              type: string
+              format: uuid
+      required:
+      - fileStateIdsByPersons
     GetPersonFileStateResponse:
       type: object
       properties:
@@ -7204,6 +7308,20 @@ components:
       - phoneNumbers
       - referenceVersion
       - salutation
+    PersonKeyAttributes:
+      type: object
+      properties:
+        dateOfBirth:
+          type: string
+          format: date
+        firstName:
+          type: string
+        lastName:
+          type: string
+      required:
+      - dateOfBirth
+      - firstName
+      - lastName
     Population:
       type: object
       properties:
@@ -7678,6 +7796,18 @@ components:
       - subject
       - text
       - to
+    SetMatterOfConcernRequest:
+      type: object
+      properties:
+        concern:
+          type: string
+          description: The matter of concern for the GDPR procedure.
+        version:
+          type: integer
+          format: int64
+      required:
+      - concern
+      - version
     ShowAs:
       type: string
       description: |2
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/PersonController.java b/backend/base/src/main/java/de/eshg/base/centralfile/PersonController.java
index 54a5b8e1bfd13e6ed4cb889ce6d244040b9116fd..a10d5bf7c7faa3c697a15b490e8b46ee841fc293 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/PersonController.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/PersonController.java
@@ -133,6 +133,27 @@ public class PersonController implements PersonApi {
     return new GetFileStateIdsResponse(searchResultsFromDb);
   }
 
+  @Override
+  @Transactional(readOnly = true)
+  public GetPersonFileStateIdsByKeyAttributesResponse
+      getPersonFileStateIdsByReferencePersonKeyAttributes(
+          GetPersonFileStateIdsByKeyAttributesRequest request) {
+    List<Person> referencePersons =
+        personService.findReferencePersonsByKeyAttributes(request.searchAttributes());
+
+    Map<PersonKeyAttributes, List<UUID>> result = new LinkedHashMap<>();
+    for (Person referencePerson : referencePersons) {
+      PersonKeyAttributes key =
+          new PersonKeyAttributes(
+              referencePerson.getFirstName(),
+              referencePerson.getLastName(),
+              referencePerson.getBirthDetails().dateOfBirth());
+      result.put(
+          key, personRepository.findAllByReferencePersonIdOrderById(referencePerson.getId()));
+    }
+    return new GetPersonFileStateIdsByKeyAttributesResponse(result);
+  }
+
   @Override
   @Transactional(readOnly = true)
   public GetPersonFileStatesResponse getPersonFileStates(GetPersonFileStatesRequest request) {
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonKeyAttributes.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonKeyAttributes.java
deleted file mode 100644
index b2aa1bb6cb54e39bf5755285c58bcbd2686b7599..0000000000000000000000000000000000000000
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonKeyAttributes.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.base.centralfile.persistence;
-
-import java.time.LocalDate;
-
-public record PersonKeyAttributes(String firstName, String lastName, LocalDate dateOfBirth) {}
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonService.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonService.java
index adf77d0714774beb16ff31237c8619e011de363c..27413901b06e0465a5d7bffdffafcc2246ca8fe3 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonService.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/PersonService.java
@@ -10,6 +10,7 @@ import static de.eshg.base.util.SearchSpecificationUtil.getSimilarityThreshold;
 
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.base.centralfile.CentralFileAuditLogger;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.base.centralfile.persistence.entity.BirthDetails_;
 import de.eshg.base.centralfile.persistence.entity.DataOrigin;
 import de.eshg.base.centralfile.persistence.entity.Person;
@@ -108,7 +109,7 @@ public class PersonService {
   private List<UUID> addPersonFileStatesWhenLocked(List<Person> fileStates) {
     Set<PersonKeyAttributes> personKeyAttributes = collectPersonKeyAttributes(fileStates);
 
-    List<Person> potentialMatches = findAllByKeyAttributes(personKeyAttributes);
+    List<Person> potentialMatches = findReferencePersonsByKeyAttributes(personKeyAttributes);
 
     Map<PersonKeyAttributes, Person> lowestIdPersons = createLowestIdMap(potentialMatches);
 
@@ -314,7 +315,7 @@ public class PersonService {
     return personRepository.findAllByExternalIdInAndReferencePersonIsNotNullOrderById(fileStateIds);
   }
 
-  public List<Person> findAllByKeyAttributes(Set<PersonKeyAttributes> keyAttributes) {
+  public List<Person> findReferencePersonsByKeyAttributes(Set<PersonKeyAttributes> keyAttributes) {
     Specification<Person> personSpecification =
         (root, query, criteriaBuilder) -> {
           root.fetch(Person_.emailAddresses, JoinType.LEFT);
@@ -323,14 +324,14 @@ public class PersonService {
 
           List<Predicate> conjunctions = new ArrayList<>();
 
-          for (PersonKeyAttributes pair : keyAttributes) {
+          for (PersonKeyAttributes key : keyAttributes) {
             conjunctions.add(
                 criteriaBuilder.and(
-                    criteriaBuilder.equal(root.get(Person_.firstName), pair.firstName()),
-                    criteriaBuilder.equal(root.get(Person_.lastName), pair.lastName()),
+                    criteriaBuilder.equal(root.get(Person_.firstName), key.firstName()),
+                    criteriaBuilder.equal(root.get(Person_.lastName), key.lastName()),
                     criteriaBuilder.equal(
                         root.get(Person_.birthDetails).get(BirthDetails_.dateOfBirth),
-                        pair.dateOfBirth()),
+                        key.dateOfBirth()),
                     criteriaBuilder.isNull(root.get(Person_.referencePerson)),
                     criteriaBuilder.notEqual(root.get(Person_.dataOrigin), DataOrigin.EXTERNAL)));
           }
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
index 356ff070e9f8196939f395ec75e7b321e1d0cdb2..eca9504c7cf643fa9892e70ce96fa724a325ff7c 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
@@ -20,7 +20,9 @@ import de.eshg.base.gdpr.api.*;
 import de.eshg.base.gdpr.persistence.*;
 import de.eshg.base.util.MappingUtil;
 import de.eshg.base.util.PaginationUtil;
+import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
+import de.eshg.validation.ValidationUtil;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.List;
 import java.util.UUID;
@@ -58,7 +60,8 @@ public class GdprProcedureController implements GdprProcedureApi {
         mapToApi(gdprProcedure.getStatus()),
         mapToApi(gdprProcedure.getType()),
         mapToApi(gdprProcedure.getIdentificationData()),
-        gdprProcedure.getCreatedAt());
+        gdprProcedure.getCreatedAt(),
+        gdprProcedure.getMatterOfConcern());
   }
 
   private static GdprIdentificationDataDto mapToApi(IdentificationData identificationData) {
@@ -235,6 +238,43 @@ public class GdprProcedureController implements GdprProcedureApi {
         service.addCentralFileIdToGdprProcedure(request.centralFileId(), id, request.version()));
   }
 
+  @Override
+  @Transactional
+  public void setMatterOfConcern(UUID id, SetMatterOfConcernRequest request) {
+    GdprProcedure procedure = service.getGdprProcedureForUpdate(id);
+    ValidationUtil.validateVersion(request.version(), procedure);
+    procedure.setMatterOfConcern(request.concern());
+  }
+
+  @Override
+  @Transactional
+  public void changeStatus(UUID id, GdprProcedureChangeStatusRequest request) {
+    GdprProcedure procedure = service.getGdprProcedureForUpdate(id);
+
+    if (procedure.getType() != GdprProcedureType.RIGHT_TO_OBJECT) {
+      throw new BadRequestException(
+          "Changing the status of GDPR procedures with type '"
+              + procedure.getType()
+              + "' is not supported yet.");
+    }
+
+    ValidationUtil.validateVersion(request.version(), procedure);
+
+    if (procedure.getStatus() == GdprProcedureStatus.DRAFT) {
+      if (request.newStatus() != GdprProcedureStatusDto.IN_PROGRESS) {
+        throw badStatusTransition(request.newStatus(), procedure.getStatus());
+      }
+
+      if (procedure.getMatterOfConcern() == null) {
+        throw new BadRequestException("Cannot start procedure without valid matter of concern.");
+      }
+
+      procedure.setStatus(GdprProcedureStatus.IN_PROGRESS);
+    } else {
+      throw badStatusTransition(request.newStatus(), procedure.getStatus());
+    }
+  }
+
   public GetGdprProceduresResponse mapGdprProceduresToApi(Page<GdprProcedure> procedures) {
     return new GetGdprProceduresResponse(
         procedures.stream().map(GdprProcedureController::mapGdprProcedureToApi).toList(),
@@ -257,4 +297,14 @@ public class GdprProcedureController implements GdprProcedureApi {
       case GdprProcedureSortKey.CREATED_AT -> GdprProcedure_.CREATED_AT;
     };
   }
+
+  private static BadRequestException badStatusTransition(
+      GdprProcedureStatusDto wantedStatus, GdprProcedureStatus currentStatus) {
+    return new BadRequestException(
+        "Status cannot be changed to '"
+            + wantedStatus
+            + "' while current status is '"
+            + currentStatus
+            + "'.");
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
index b97f0644fa9bb294f09f83a2fdfcc0610fd8e68f..5b63621db65c1758a3b2b9e5b9265f2947e90a0f 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
@@ -77,7 +77,7 @@ public class GdprProcedureService {
     return repository.findByExternalId(gdprProcedureId).orElseThrow(notFound(gdprProcedureId));
   }
 
-  private GdprProcedure getGdprProcedureForUpdate(UUID gdprProcedureId) {
+  public GdprProcedure getGdprProcedureForUpdate(UUID gdprProcedureId) {
     return repository
         .findByExternalIdForUpdate(gdprProcedureId)
         .orElseThrow(notFound(gdprProcedureId));
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
index d5c79f85fc2969d7f36ffb7bebd5a065456ca4af..76e8f0577217ba377ea0be38e8a10485bcc670de 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
@@ -53,6 +53,9 @@ public class GdprProcedure extends BaseEntityWithExternalId {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private IdentificationData identificationData;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  private String matterOfConcern;
+
   public UUID getCentralFileId() {
     return centralFileId;
   }
@@ -116,4 +119,12 @@ public class GdprProcedure extends BaseEntityWithExternalId {
   public void setIdentificationData(IdentificationData identificationData) {
     this.identificationData = identificationData;
   }
+
+  public String getMatterOfConcern() {
+    return matterOfConcern;
+  }
+
+  public void setMatterOfConcern(String matterOfConcern) {
+    this.matterOfConcern = matterOfConcern;
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java
index 943921e5c65e84d8732e9c0793b620fd3343581f..04b2321f879c357a7d0bc9e5addf3ce54ae8bf8e 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java
@@ -28,7 +28,7 @@ public enum ModuleClient {
   SCHOOL_ENTRY("school-entry", List.of(BASE_MAIL_SEND)),
   STATISTICS("statistics", List.of(STATISTICS_STATISTICS_WRITE)),
   TRAVEL_MEDICINE("travel-medicine", List.of(BASE_MAIL_SEND, BASE_ACCESS_CODE_USER_ADMIN)),
-  STI_PROTECTION("sti-protection");
+  STI_PROTECTION("sti-protection", List.of(BASE_MAIL_SEND));
 
   private final String clientIdWithoutPrefix;
   private final List<EmployeePermissionRole> roles;
diff --git a/backend/base/src/main/resources/migrations/0015_gdpr_matter_of_concern.xml b/backend/base/src/main/resources/migrations/0015_gdpr_matter_of_concern.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eb9b333f17712a6334165717a3b1be89deb44362
--- /dev/null
+++ b/backend/base/src/main/resources/migrations/0015_gdpr_matter_of_concern.xml
@@ -0,0 +1,13 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: Apache-2.0
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1727369112763-1">
+    <addColumn tableName="gdpr_procedure">
+      <column name="matter_of_concern" type="text"/>
+    </addColumn>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/base/src/main/resources/migrations/changelog.xml b/backend/base/src/main/resources/migrations/changelog.xml
index 511fef83c76894702ff0a930ddbeb35263f106e5..5fb2e561ff518c94fefa0f9c8a1a39116a2ce2b9 100644
--- a/backend/base/src/main/resources/migrations/changelog.xml
+++ b/backend/base/src/main/resources/migrations/changelog.xml
@@ -22,5 +22,6 @@
     <include file="migrations/0012_rename_single_string_entity_columns.xml"/>
     <include file="migrations/0013_person_sequence.xml"/>
     <include file="migrations/0014_contact_index_full_name.xml" />
+    <include file="migrations/0015_gdpr_matter_of_concern.xml" />
 
 </databaseChangeLog>
diff --git a/backend/buildSrc/src/main/groovy/eshg.service.gradle b/backend/buildSrc/src/main/groovy/eshg.service.gradle
index 2ffde22d6b5f58d1217eff06233cb8336326f09f..25f6a742212db2a523554b0e6bed72baa72294fc 100644
--- a/backend/buildSrc/src/main/groovy/eshg.service.gradle
+++ b/backend/buildSrc/src/main/groovy/eshg.service.gradle
@@ -12,7 +12,7 @@ plugins {
 }
 
 bootRun {
-    systemProperty 'spring.profiles.active', 'test-helper'
+    systemProperty 'spring.profiles.active', getActiveSpringProfiles()
 }
 
 def dockerBuildDir = layout.buildDirectory.dir('docker')
@@ -165,12 +165,21 @@ rootProject.tasks.named("composeUp").configure {
     dependsOn tasks.named("composeUp")
 }
 
+String getActiveSpringProfiles() {
+  String defaultSpringProfiles = "local, test-helper"
+  if (project.hasProperty("preview-features") || project.hasProperty("previewFeatures")) {
+    return "${defaultSpringProfiles}, preview-features"
+  } else {
+    return defaultSpringProfiles
+  }
+}
+
 dockerCompose {
     projectName = rootProject.name
     useComposeFiles = ["${rootDir}/docker-compose.yaml"]
 
     if (project.hasProperty("preview-features") || project.hasProperty("previewFeatures")) {
-        environment.put "ACTIVE_SPRING_PROFILES", "test-helper, preview-features"
+        environment.put "ACTIVE_SPRING_PROFILES", getActiveSpringProfiles()
     }
 }
 
diff --git a/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/DefaultEshgSecurityConfig.java b/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/DefaultEshgSecurityConfig.java
index 60ac6ad4b116a529b6072303e5e5636392018bc5..730a66829944298e2070de0c465a8efc26f4e800 100644
--- a/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/DefaultEshgSecurityConfig.java
+++ b/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/DefaultEshgSecurityConfig.java
@@ -45,6 +45,7 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut
 import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.intercept.AuthorizationFilter;
 import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy;
 import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
@@ -111,6 +112,7 @@ public class DefaultEshgSecurityConfig {
         .csrf(AbstractHttpConfigurer::disable)
         .sessionManagement(AbstractHttpConfigurer::disable)
         .headers(DefaultEshgSecurityConfig::securityHeaders)
+        .addFilterAfter(new UserSessionIdLoggingFilter(), AuthorizationFilter.class)
         .build();
   }
 
diff --git a/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/UserSessionIdLoggingFilter.java b/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/UserSessionIdLoggingFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..df5cb35f88b03166e8379d4d35b09b5b4866c473
--- /dev/null
+++ b/backend/business-module-commons/src/main/java/de/eshg/rest/service/security/UserSessionIdLoggingFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.rest.service.security;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.Closeable;
+import java.io.IOException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+public class UserSessionIdLoggingFilter extends OncePerRequestFilter {
+  private static final Logger log = LoggerFactory.getLogger(UserSessionIdLoggingFilter.class);
+
+  private static final int SESSION_ID_TRUNCATION_LENGTH = "aaaaaaaa-bbbb".length();
+
+  @Override
+  protected void doFilterInternal(
+      HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+      throws ServletException, IOException {
+    String userSessionId =
+        CurrentUserHelper.getCurrentUserSessionIdGracefully()
+            .map(UserSessionIdLoggingFilter::truncateUuid)
+            .orElse(null);
+    try (Closeable closeable = putCloseableIfNotNull("userSessionId", userSessionId)) {
+      log.trace("{}", closeable);
+      filterChain.doFilter(request, response);
+    }
+  }
+
+  // For security reasons we truncate the UUID from "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" to
+  // "aaaaaaaa-bbbb"
+  private static String truncateUuid(String sessionId) {
+    return StringUtils.left(sessionId, SESSION_ID_TRUNCATION_LENGTH) + "…";
+  }
+
+  private static Closeable putCloseableIfNotNull(String key, String value) {
+    if (value == null) {
+      return () -> {};
+    }
+    return MDC.putCloseable(key, value);
+  }
+}
diff --git a/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/UserSessionIdPrefixNormalizer.java b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/UserSessionIdPrefixNormalizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..68e556b6b5cf1d29f3026dd0cac4867747c862f0
--- /dev/null
+++ b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/UserSessionIdPrefixNormalizer.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg;
+
+import de.cronn.assertions.validationfile.normalization.IdNormalizer;
+import de.cronn.assertions.validationfile.normalization.IncrementingIdProvider;
+import de.cronn.assertions.validationfile.normalization.ValidationNormalizer;
+
+class UserSessionIdPrefixNormalizer implements ValidationNormalizer {
+
+  private final ValidationNormalizer delegate =
+      new IdNormalizer(
+          new IncrementingIdProvider(),
+          "USER_SESSION_",
+          "(?<=userSessionId=)([0-9a-f]{8}-[0-9a-f]{4}…)");
+
+  @Override
+  public String normalize(String source) {
+    return delegate.normalize(source);
+  }
+}
diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml
index 8cf8939f7db28da05e4e4e4ce75d40f44f9994d5..97ffabbbaa42088111a29aafc6d308049e54e019 100644
--- a/backend/docker-compose.yaml
+++ b/backend/docker-compose.yaml
@@ -17,6 +17,8 @@ services:
   auth-employee-portal:
     extends:
       service: auth-base
+    environment:
+      - spring.profiles.active=local, employee-portal
     ports:
       - 8092:8080
 
@@ -24,7 +26,7 @@ services:
     extends:
       service: auth-base
     environment:
-      - spring.profiles.active=citizen-portal
+      - spring.profiles.active=local, citizen-portal
       - spring.data.redis.database=1
       # use a different SESSION cookie such that browsers don’t confuse the session
       # when opening http://localhost:4000 and http://localhost:4001 in parallel
@@ -49,7 +51,7 @@ services:
     image: ga-lotse/base
     environment:
       - DOCKER_HOSTNAME=${DOCKER_HOSTNAME:-localhost}
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://base-db/eshgbase
       - eshg.keycloak.internal.url=http://keycloak:8080
       - eshg.keycloak.admin-client.client-secret=admin
@@ -89,6 +91,7 @@ services:
 
   keycloak-reverse-proxy:
     image: nginx:1.27.1
+    restart: unless-stopped
     ports:
       - 4003:4003
     read_only: true
@@ -141,7 +144,7 @@ services:
     env_file:
       - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://inspection-db/inspection
       - de.eshg.base.service-url=http://base:8080
       - de.eshg.centralrepository.service-url=http://central-repository:8080
@@ -171,7 +174,7 @@ services:
     env_file:
       - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://school-entry-db/schoolentry
       - de.eshg.base.service-url=http://base:8080
       - eshg.keycloak.internal.url=http://keycloak:8080
@@ -199,7 +202,7 @@ services:
   service-directory:
     image: ga-lotse/service-directory
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://service-directory-db/servicedirectory
     restart: unless-stopped
     depends_on:
@@ -224,7 +227,8 @@ services:
     env_file:
       - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - DOCKER_HOSTNAME=${DOCKER_HOSTNAME:-localhost}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://travel-medicine-db/travelmedicine
       - de.eshg.base.service-url=http://base:8080
       - eshg.keycloak.internal.url=http://keycloak:8080
@@ -239,6 +243,8 @@ services:
       - de.eshg.travel-medicine.department-info.phoneNumber=+49 123 12345678
       - de.eshg.travel-medicine.department-info.homepage=www.travel-medicine.de
       - de.eshg.travel-medicine.department-info.email=travel@medicine.de
+      - de.eshg.travel-medicine.notification.fromAddress=info.reisemedizin@stadt-frankfurt.de
+      - de.eshg.travel-medicine.notification.greeting=Ihr Impfberatungsteam der Stadt Frankfurt
     restart: unless-stopped
     depends_on:
       travel-medicine-db:
@@ -264,7 +270,7 @@ services:
       - eshg.keycloak.internal.url=http://keycloak:8080
       - eshg.servicedirectory.baseUrl=http://service-directory:8080
       - de.eshg.servicedirectory.mock-cert-subject-cn=dummy
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
     restart: unless-stopped
     depends_on:
       keycloak:
@@ -279,7 +285,7 @@ services:
     env_file:
       - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://measles-protection-db/measles_protection
       - de.eshg.base.service-url=http://base:8080
       - eshg.keycloak.internal.url=http://keycloak:8080
@@ -305,7 +311,7 @@ services:
   statistics:
     image: ga-lotse/statistics
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://statistics-db/statistics
       - eshg.keycloak.internal.url=http://keycloak:8080
       - de.eshg.base.service-url=http://base:8080
@@ -333,7 +339,7 @@ services:
   central-repository:
     image: ga-lotse/central-repository
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://central-repository-db/centralrepository
       - eshg.servicedirectory.baseUrl=http://service-directory:8080
       - de.eshg.servicedirectory.mock-cert-subject-cn=dummy
@@ -361,7 +367,7 @@ services:
     image: ga-lotse/chat-management
     environment:
       - synapse.url=http://synapse:8008
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://chat-management-db/chat_management
       - de.eshg.base.service-url=http://base:8080
       - eshg.keycloak.internal.url=http://keycloak:8080
@@ -429,7 +435,7 @@ services:
       - spring.datasource.url=jdbc:postgresql://auditlog-db/auditlog
       - de.eshg.base.service-url=http://base:8080
       - de.eshg.auditlog.log-storage-dir=/tmp/auditlog-storage
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - eshg.keycloak.internal.url=http://keycloak:8080
 
   auditlog-db:
@@ -448,7 +454,7 @@ services:
     env_file:
       - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
-      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-test-helper}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://sti-protection-db/sti_protection
       - de.eshg.base.service-url=http://base:8080
       - eshg.keycloak.internal.url=http://keycloak:8080
@@ -476,13 +482,12 @@ services:
     ports:
       - 8096:8080
     environment:
-      DOCKER_HOSTNAME: ${DOCKER_HOSTNAME:-localhost}
-      spring.datasource.url: jdbc:postgresql://opendata-db/opendata
-      de.eshg.base.service-url: http://base:8080
-      de.eshg.auditlog.log-storage-dir: /tmp/opendata-storage
-      spring.profiles.active: test-helper
-      eshg.testclock.enabled: true
-      eshg.keycloak.internal.url: http://keycloak:8080
+      - DOCKER_HOSTNAME=${DOCKER_HOSTNAME:-localhost}
+      - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
+      - spring.datasource.url=jdbc:postgresql://opendata-db/opendata
+      - de.eshg.base.service-url=http://base:8080
+      - de.eshg.auditlog.log-storage-dir=/tmp/opendata-storage
+      - eshg.keycloak.internal.url=http://keycloak:8080
 
   opendata-db:
     extends:
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionPopulator.java b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionPopulator.java
index f442caf2098bae7add54b1ff1a21ddd6b769dd86..ffb137ed7c07839116e9a8e9aed106c2961583de 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionPopulator.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionPopulator.java
@@ -12,6 +12,7 @@ import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
 import de.eshg.testhelper.population.BasePopulator;
 import de.eshg.testhelper.population.ListWithTotalNumber;
 import de.eshg.testhelper.population.PopulateWithAccessTokenHelper;
+import de.eshg.testhelper.population.RequestContextFaker;
 import java.time.Clock;
 import net.datafaker.Faker;
 import org.springframework.core.env.Environment;
@@ -61,7 +62,11 @@ public class InspectionPopulator extends BasePopulator<InspectionDto> {
       Faker faker,
       BasePopulator<InspectionDto>.UniqueValueProvider uniqueValueProvider) {
     InspectionDto response = facilityTestDataProvider.createTestFacilityAndStartInsp(index);
-    inspectionTestDataProvider.prepareTestInspection(response.externalId(), faker, index);
+    RequestContextFaker.withFakedRequestContextIfNecessary(
+        () -> {
+          inspectionTestDataProvider.prepareTestInspection(response.externalId(), faker, index);
+          return null;
+        });
     return response;
   }
 
diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css b/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css
index ebe3a228f84c7a416227c69838d49ae338c7b62c..95e046c3c3e9cf415e600ea49e58eed9d0f3a5f9 100644
--- a/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css
+++ b/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css
@@ -176,6 +176,10 @@ a {
     }
 }
 
+#kc-current-locale-link {
+  display: none;
+}
+
 #kc-page-title {
     text-align: left;
     align-self: self-start;
diff --git a/backend/lib-aggregation/build.gradle b/backend/lib-aggregation/build.gradle
index 8d0f608c431799c5a2f886cc9792d9e5ec19d952..0332c2145f1d47b87051188aeb68c9665bbe996e 100644
--- a/backend/lib-aggregation/build.gradle
+++ b/backend/lib-aggregation/build.gradle
@@ -5,6 +5,7 @@ plugins {
 
 dependencies {
     implementation project(':business-module-commons')
+    implementation project(':logging-commons')
 
     api project(':lib-statistics-api')
     api project(':lib-procedures-api')
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AggregationHelper.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AggregationHelper.java
index d448581ebc85aa60a745b34ff52d8c3e30f6f8bd..1b178d604b462f39a4c07d8d339df560f3e1549d 100644
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AggregationHelper.java
+++ b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AggregationHelper.java
@@ -44,8 +44,8 @@ public abstract class AggregationHelper {
   <R, C extends ClientWithLocationAndTimeout> List<ClientResponse<R>> requestFromClients(
       List<C> clients, Function<C, R> getFromClient) {
     try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
-      DelegatingSecurityContextExecutor delegatingSecurityContextExecutor =
-          new DelegatingSecurityContextExecutor(executorService);
+      Executor executor =
+          new CorrelationIdAwareExecutor(new DelegatingSecurityContextExecutor(executorService));
 
       Map<String, Future<R>> futures =
           clients.stream()
@@ -56,7 +56,7 @@ public abstract class AggregationHelper {
                           getAsyncOrTimout(
                               () -> getFromClient.apply(client),
                               client.getClientTimeout(),
-                              delegatingSecurityContextExecutor)));
+                              executor)));
 
       try {
         return futures.entrySet().stream()
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleAggregationHelper.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleAggregationHelper.java
index f5ead3e4dd169920dd39ea672540de51bf6fed8b..bb31981175e0deea3ebc4b647dec53d982ebc202 100644
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleAggregationHelper.java
+++ b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleAggregationHelper.java
@@ -75,8 +75,8 @@ public class BusinessModuleAggregationHelper extends AggregationHelper {
     }
   }
 
-  public <T2> List<ClientResponse<T2>> requestFromBusinessModulesClients(
-      Set<String> businessModules, Function<BusinessModuleClient, T2> getFromBusinessModule) {
+  public <T> List<ClientResponse<T>> requestFromBusinessModulesClients(
+      Set<String> businessModules, Function<BusinessModuleClient, T> getFromBusinessModule) {
     List<BusinessModuleClient> businessModuleClients =
         businessModuleClientRegistry.getBusinessModuleClients().stream()
             .filter(client -> bySetContainingValue(businessModules, client::getLocation))
@@ -84,9 +84,9 @@ public class BusinessModuleAggregationHelper extends AggregationHelper {
     return requestFromClients(businessModuleClients, getFromBusinessModule);
   }
 
-  public <T2> List<ClientResponse<T2>> requestFromBusinessModules(
+  public <T> List<ClientResponse<T>> requestFromBusinessModules(
       Set<BusinessModule> businessModules,
-      Function<BusinessModuleClient, T2> getFromBusinessModule) {
+      Function<BusinessModuleClient, T> getFromBusinessModule) {
     Set<String> businessModuleNames =
         businessModules == null
             ? null
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleClient.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleClient.java
index 610d3a559c8a886f0f359f2b2ad5cce7d639f013..57c9485795284928b48e69df38f5a73f7c5ddefa 100644
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleClient.java
+++ b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/BusinessModuleClient.java
@@ -30,6 +30,7 @@ import de.eshg.lib.statistics.api.GetDataSourcesResponse;
 import de.eshg.lib.statistics.api.GetSpecificDataRequest;
 import de.eshg.lib.statistics.api.GetSpecificDataResponse;
 import de.eshg.rest.client.AccessTokenForwardingInterceptor;
+import de.eshg.rest.client.CorrelationIdForwardingInterceptor;
 import de.eshg.rest.client.SimpleModelAttributeArgumentResolver;
 import java.time.Duration;
 import java.time.Instant;
@@ -93,6 +94,7 @@ public class BusinessModuleClient extends ClientWithLocationAndTimeout
         restClientBuilder
             .baseUrl(url)
             .requestInterceptor(new AccessTokenForwardingInterceptor())
+            .requestInterceptor(new CorrelationIdForwardingInterceptor())
             .build();
 
     RestClientAdapter restClientAdapter = RestClientAdapter.create(restClient);
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/CorrelationIdAwareExecutor.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/CorrelationIdAwareExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..36381de296b5e30b4d2316a99807cc4d147374f2
--- /dev/null
+++ b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/CorrelationIdAwareExecutor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.aggregation;
+
+import de.eshg.logging.LoggingConstants;
+import java.util.concurrent.Executor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+public class CorrelationIdAwareExecutor implements Executor {
+
+  private static final Logger log = LoggerFactory.getLogger(CorrelationIdAwareExecutor.class);
+
+  private final Executor executor;
+
+  public CorrelationIdAwareExecutor(Executor executor) {
+    this.executor = executor;
+  }
+
+  @Override
+  public final void execute(Runnable task) {
+    this.executor.execute(wrap(task));
+  }
+
+  private static Runnable wrap(Runnable task) {
+    String correlationId = MDC.get(LoggingConstants.CORRELATION_ID_MDC_KEY);
+    if (correlationId == null) {
+      return task;
+    }
+    return () -> {
+      try (MDC.MDCCloseable mdcCloseable =
+          MDC.putCloseable(LoggingConstants.CORRELATION_ID_MDC_KEY, correlationId)) {
+        log.trace("{}", mdcCloseable);
+        task.run();
+      }
+    };
+  }
+}
diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java
index 1b9a40f2027a5b5e0ce736096c9814f26fbdac95..deff229c1d465d38646f5695cd2d30154e09c824 100644
--- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java
+++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java
@@ -13,6 +13,7 @@ import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.auditlog.DefaultUuidProvider;
 import de.eshg.lib.auditlog.config.AuditLogConfig;
 import de.eshg.rest.client.AccessTokenForwardingInterceptor;
+import de.eshg.rest.client.CorrelationIdForwardingInterceptor;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -45,6 +46,7 @@ public class AuditLogAutoConfiguration {
         restClientBuilder
             .baseUrl(auditLogConfig.getServiceUrl())
             .requestInterceptor(new AccessTokenForwardingInterceptor())
+            .requestInterceptor(new CorrelationIdForwardingInterceptor())
             .build();
 
     RestClientAdapter restClientAdapter = RestClientAdapter.create(restTemplate);
diff --git a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
index 7505359c06b1764018fa7f4af26796ccda605b46..ee3ba9b145a389355584e0d544ae405c606466ce 100644
--- a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
+++ b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
@@ -20,6 +20,7 @@ import de.eshg.base.statistics.BaseStatisticsApi;
 import de.eshg.base.testhelper.BaseTestHelperApi;
 import de.eshg.base.user.UserApi;
 import de.eshg.rest.client.AccessTokenForwardingInterceptor;
+import de.eshg.rest.client.CorrelationIdForwardingInterceptor;
 import de.eshg.rest.client.SimpleModelAttributeArgumentResolver;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
 import de.eshg.testhelper.TestHelperAutoConfiguration;
@@ -127,6 +128,7 @@ class BaseClientAutoConfiguration {
         restClientBuilder
             .baseUrl(baseClientProperties.getServiceUrl())
             .requestInterceptor(new AccessTokenForwardingInterceptor())
+            .requestInterceptor(new CorrelationIdForwardingInterceptor())
             .build();
     RestClientAdapter restClientAdapter = RestClientAdapter.create(restClient);
 
diff --git a/backend/lib-central-repository-client/src/main/java/de/eshg/centralrepository/client/CentralRepositoryClientAutoConfiguration.java b/backend/lib-central-repository-client/src/main/java/de/eshg/centralrepository/client/CentralRepositoryClientAutoConfiguration.java
index 11efa1b2a8f05d6453e59295cdf53addc7034a5a..df4f6c46b8ddd46813114b2ca4251a47b2a15e5c 100644
--- a/backend/lib-central-repository-client/src/main/java/de/eshg/centralrepository/client/CentralRepositoryClientAutoConfiguration.java
+++ b/backend/lib-central-repository-client/src/main/java/de/eshg/centralrepository/client/CentralRepositoryClientAutoConfiguration.java
@@ -6,12 +6,12 @@
 package de.eshg.centralrepository.client;
 
 import de.eshg.rest.client.AccessTokenForwardingInterceptor;
+import de.eshg.rest.client.CorrelationIdForwardingInterceptor;
 import io.micrometer.common.util.StringUtils;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 import org.springframework.web.client.RestClient;
 
 @AutoConfiguration
@@ -21,13 +21,12 @@ public class CentralRepositoryClientAutoConfiguration {
 
   @Bean
   CentralRepositoryRestClient centralRepositoryRestClient(
-      RestClient.Builder restClientBuilder,
-      CentralRepositoryClientProperties properties,
-      Environment env) {
+      RestClient.Builder restClientBuilder, CentralRepositoryClientProperties properties) {
 
     restClientBuilder
         .baseUrl(properties.getServiceUrl())
-        .requestInterceptor(new AccessTokenForwardingInterceptor());
+        .requestInterceptor(new AccessTokenForwardingInterceptor())
+        .requestInterceptor(new CorrelationIdForwardingInterceptor());
 
     if (StringUtils.isNotEmpty(properties.getMockCertSubjectCn())) {
       restClientBuilder.requestInterceptor(
diff --git a/backend/lib-document-generator/src/main/java/de/eshg/lib/document/generator/department/DepartmentClient.java b/backend/lib-document-generator/src/main/java/de/eshg/lib/document/generator/department/DepartmentClient.java
index 28eca7352f7acfa7adc1e756b7415da25ef9fd01..e9ea736a9cf6da2265a2c858e20d79b392a6a6cf 100644
--- a/backend/lib-document-generator/src/main/java/de/eshg/lib/document/generator/department/DepartmentClient.java
+++ b/backend/lib-document-generator/src/main/java/de/eshg/lib/document/generator/department/DepartmentClient.java
@@ -14,21 +14,35 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
+import org.springframework.web.context.annotation.RequestScope;
 
 @Component
+@RequestScope
 public class DepartmentClient {
 
   private final DepartmentApi departmentApi;
+  private GetDepartmentInfoResponse cachedDepartmentInfo;
+  private DepartmentLogo cachedDepartmentLogo;
 
   public DepartmentClient(DepartmentApi departmentApi) {
     this.departmentApi = departmentApi;
   }
 
   public GetDepartmentInfoResponse getDepartmentInfo() {
-    return departmentApi.getDepartmentInfo();
+    if (cachedDepartmentInfo == null) {
+      cachedDepartmentInfo = departmentApi.getDepartmentInfo();
+    }
+    return cachedDepartmentInfo;
   }
 
   public DepartmentLogo getDepartmentLogo() {
+    if (cachedDepartmentLogo == null) {
+      cachedDepartmentLogo = fetchDepartmentLogo();
+    }
+    return cachedDepartmentLogo;
+  }
+
+  public DepartmentLogo fetchDepartmentLogo() {
     ResponseEntity<Resource> departmentLogoResponse = departmentApi.getDepartmentLogo();
     Assert.isTrue(
         departmentLogoResponse.getStatusCode().equals(HttpStatus.OK),
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
index 06e0c5ef8f4ef76d9869d9d3412acb530a368fae..e826913d5c35fa2121af40f8acfa448480482b52 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
@@ -171,9 +171,31 @@ public interface ProcedureRepository<ProcedureT extends Procedure<ProcedureT, ?,
   List<ProcedureT> findByRelatedPersonsCentralFileStateIdInOrderByCreatedAtDescIdAsc(
       Collection<UUID> centralFileStateIds);
 
-  List<ProcedureT>
-      findByRelatedPersonsCentralFileStateIdInAndRelatedPersonsPersonTypeOrderByCreatedAtDescIdAsc(
-          Collection<UUID> centralFileStateIds, PersonType personType);
+  @Query(
+      """
+ SELECT procedure from #{#entityName} procedure
+JOIN procedure.relatedPersons relatedPerson
+WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
+AND relatedPerson.personType = :personType
+ORDER BY procedure.createdAt DESC, procedure.id ASC
+""")
+  List<ProcedureT> findByRelatedPersonsCentralFileStateIds(
+      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
+      @Param("personType") PersonType personType);
+
+  @Query(
+      """
+    SELECT procedure from #{#entityName} procedure
+    JOIN procedure.relatedPersons relatedPerson
+    WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
+    AND relatedPerson.personType = :personType
+    AND procedure.procedureStatus = :procedureStatus
+    ORDER BY procedure.createdAt DESC, procedure.id ASC
+    """)
+  List<ProcedureT> findByRelatedPersonsCentralFileStateIds(
+      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
+      @Param("personType") PersonType personType,
+      @Param("procedureStatus") ProcedureStatus procedureStatus);
 
   List<ProcedureT> findAllByArchivingRelevance(ArchivingRelevance archivingRelevance);
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
index 764e585e55bfe7a394cb2a5b95a309fe581cd237..52d4a2c3a897f13165974235710781731e8fdcf6 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
@@ -12,8 +12,10 @@ import de.eshg.base.centralfile.PersonApi;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStatesRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
+import de.eshg.base.centralfile.api.person.GetPersonFileStateIdsByKeyAttributesRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.centralfile.api.person.GetReferencePersonResponse;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.procedure.domain.model.PersonType;
 import de.eshg.lib.procedure.domain.model.Procedure;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
@@ -26,10 +28,13 @@ import java.time.LocalDate;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.EnumSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.Set;
 import java.util.StringJoiner;
 import java.util.UUID;
 import java.util.function.Function;
@@ -104,6 +109,27 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
     return foundProcedures;
   }
 
+  public Map<PersonKeyAttributes, List<ProcedureT>> searchOpenProceduresByPersons(
+      Set<PersonKeyAttributes> searchAttributes, PersonType personType) {
+    if (searchAttributes.isEmpty()) {
+      return Map.of();
+    }
+    Map<PersonKeyAttributes, List<UUID>> fileStateIdsByPersonAttributes =
+        personApi
+            .getPersonFileStateIdsByReferencePersonKeyAttributes(
+                new GetPersonFileStateIdsByKeyAttributesRequest(searchAttributes))
+            .fileStateIdsByPersons();
+
+    Map<PersonKeyAttributes, List<ProcedureT>> result = new LinkedHashMap<>();
+    for (Entry<PersonKeyAttributes, List<UUID>> entry : fileStateIdsByPersonAttributes.entrySet()) {
+      List<ProcedureT> procedures =
+          procedureRepository.findByRelatedPersonsCentralFileStateIds(
+              entry.getValue(), personType, ProcedureStatus.OPEN);
+      result.put(entry.getKey(), procedures);
+    }
+    return result;
+  }
+
   public List<ProcedureT> searchProceduresByPerson(
       String firstName, String lastName, LocalDate dateOfBirth, PersonType personType) {
     List<UUID> fileStateIds =
@@ -116,9 +142,7 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
                         .fileStateIds())
             .flatMap(Collection::stream)
             .toList();
-    return procedureRepository
-        .findByRelatedPersonsCentralFileStateIdInAndRelatedPersonsPersonTypeOrderByCreatedAtDescIdAsc(
-            fileStateIds, personType);
+    return procedureRepository.findByRelatedPersonsCentralFileStateIds(fileStateIds, personType);
   }
 
   private Function<ProcedureT, SearchableProcedure<ProcedureT>> formatAsSearchable(
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
index 61399343dfa0b06e938a15b7a1bc04b455a12dd2..35aaaebd5e999833937d2a8766dcd16aa752b402 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
@@ -71,6 +71,8 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
         .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ);
     requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE);
+    requestMatchers(PUT, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE);
   }
 
   private void contacts() {
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
index 8d83fac7d0df5009e57c757c263979395e90e2db..97900114732042bf1292e60ba92a3015a62c3937 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
@@ -63,6 +63,8 @@ public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurity
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
+    requestMatchers(DELETE, BaseUrls.Statistics.REPORT_URL + "/**")
+        .hasRole(EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
 
     requestMatchers(BaseUrls.Statistics.DATA_SOURCE_CONTROLLER + "/**")
         .hasAnyRole(
diff --git a/backend/logging-commons/README_LICENSE.adoc b/backend/logging-commons/README_LICENSE.adoc
new file mode 100644
index 0000000000000000000000000000000000000000..87f2419aaf60835f287ea4b3d058bd1a2cd01097
--- /dev/null
+++ b/backend/logging-commons/README_LICENSE.adoc
@@ -0,0 +1,5 @@
+== Licensing
+
+All files within this directory, including those in all subdirectories, are licensed under the Apache License 2.0.
+
+For the complete license text, please refer to the `LICENSE-APACHE-2.0.txt` file located in the project root.
diff --git a/backend/logging-commons/build.gradle b/backend/logging-commons/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..16abd18c2e7b1339a8106fcc35843907f8a4869f
--- /dev/null
+++ b/backend/logging-commons/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+    id "eshg.java-lib"
+}
diff --git a/backend/logging-commons/buildscript-gradle.lockfile b/backend/logging-commons/buildscript-gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..0d156738b209adc7660610a8d35b7e87ebdb8211
--- /dev/null
+++ b/backend/logging-commons/buildscript-gradle.lockfile
@@ -0,0 +1,4 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+empty=classpath
diff --git a/backend/logging-commons/gradle.lockfile b/backend/logging-commons/gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..0dccf585508f79ea672a181febfba0ac3e39fbf0
--- /dev/null
+++ b/backend/logging-commons/gradle.lockfile
@@ -0,0 +1,61 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+ch.qos.logback:logback-classic:1.5.8=testCompileClasspath,testRuntimeClasspath
+ch.qos.logback:logback-core:1.5.8=testCompileClasspath,testRuntimeClasspath
+com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
+com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
+io.micrometer:micrometer-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
+io.micrometer:micrometer-observation:1.13.4=testCompileClasspath,testRuntimeClasspath
+jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
+jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
+jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=testCompileClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy:1.14.19=testCompileClasspath,testRuntimeClasspath
+net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+org.apache.logging.log4j:log4j-api:2.23.1=testCompileClasspath,testRuntimeClasspath
+org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
+org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
+org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
+org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
+org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
+org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
+org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.objenesis:objenesis:3.3=testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.ow2.asm:asm-commons:9.6=jacocoAnt
+org.ow2.asm:asm-tree:9.6=jacocoAnt
+org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
+org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
+org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath
+org.slf4j:slf4j-api:2.0.16=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-logging:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-aop:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-beans:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-context:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-core:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-expression:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-jcl:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
+org.yaml:snakeyaml:2.2=testCompileClasspath,testRuntimeClasspath
+empty=annotationProcessor,compileClasspath,developmentOnly,productionRuntimeClasspath,runtimeClasspath,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/logging-commons/src/main/java/de/eshg/logging/LoggingConstants.java b/backend/logging-commons/src/main/java/de/eshg/logging/LoggingConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f17829d77d2ce2de7ecb62b25d64fc97c315231
--- /dev/null
+++ b/backend/logging-commons/src/main/java/de/eshg/logging/LoggingConstants.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.logging;
+
+public final class LoggingConstants {
+
+  public static final String CORRELATION_ID_HEADER = "X-Correlation-ID";
+  public static final String CORRELATION_ID_MDC_KEY = "correlationId";
+
+  private LoggingConstants() {}
+}
diff --git a/backend/relay-server/src/main/java/de/eshg/relayserver/ws/WebsocketEndpoint.java b/backend/relay-server/src/main/java/de/eshg/relayserver/ws/WebsocketEndpoint.java
index 365039d9bca18556834e3036bd75fb00723eca6d..17248ee51580f9c9954243ad9db3735ddc475ab5 100644
--- a/backend/relay-server/src/main/java/de/eshg/relayserver/ws/WebsocketEndpoint.java
+++ b/backend/relay-server/src/main/java/de/eshg/relayserver/ws/WebsocketEndpoint.java
@@ -85,7 +85,7 @@ public class WebsocketEndpoint {
   @OnMessage
   public void onPong(PongMessage pongMessage) {
     String pongPayload = StandardCharsets.UTF_8.decode(pongMessage.getApplicationData()).toString();
-    logger.info("Received pong with applicationData {} for sni {}", pongPayload, this.sni);
+    logger.debug("Received pong with applicationData {} for sni {}", pongPayload, this.sni);
     if (!pongPayload.equals(outstandingPingPayload.get())) {
       logger.warn(
           "Payload mismatch: expected {}. Ignoring pong for sni {}",
@@ -206,7 +206,7 @@ public class WebsocketEndpoint {
       return;
     }
     String id = UUID.randomUUID().toString();
-    logger.info("Sending ping for sni {} with payload {}", this.sni, id);
+    logger.debug("Sending ping for sni {} with payload {}", this.sni, id);
     try {
       session.getBasicRemote().sendPing(ByteBuffer.wrap(id.getBytes(StandardCharsets.UTF_8)));
       outstandingPingPayload.set(id);
diff --git a/backend/rest-client-commons/build.gradle b/backend/rest-client-commons/build.gradle
index 897b1bf2ef87cf0da673d7fcfa824a0c9eff24f2..03548c0e79adfec8fed6ea9145d0505bf2f3a4eb 100644
--- a/backend/rest-client-commons/build.gradle
+++ b/backend/rest-client-commons/build.gradle
@@ -10,6 +10,7 @@ dependencies {
     implementation 'org.apache.httpcomponents.client5:httpclient5'
     implementation "org.zalando:logbook-httpclient5:latest.release"
     implementation project(":api-commons")
+    implementation project(":logging-commons")
 
     runtimeOnly "org.zalando:logbook-spring-boot-starter:latest.release"
 
diff --git a/backend/rest-client-commons/src/main/java/de/eshg/rest/client/CorrelationIdForwardingInterceptor.java b/backend/rest-client-commons/src/main/java/de/eshg/rest/client/CorrelationIdForwardingInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9066787256e248347c8b82eab918b8943f4443f
--- /dev/null
+++ b/backend/rest-client-commons/src/main/java/de/eshg/rest/client/CorrelationIdForwardingInterceptor.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.rest.client;
+
+import de.eshg.logging.LoggingConstants;
+import java.io.IOException;
+import org.slf4j.MDC;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+public class CorrelationIdForwardingInterceptor implements ClientHttpRequestInterceptor {
+  @Override
+  public ClientHttpResponse intercept(
+      HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+    String correlationId = MDC.get(LoggingConstants.CORRELATION_ID_MDC_KEY);
+    if (correlationId != null) {
+      request.getHeaders().set(LoggingConstants.CORRELATION_ID_HEADER, correlationId);
+    }
+    return execution.execute(request, body);
+  }
+}
diff --git a/backend/rest-service-commons/build.gradle b/backend/rest-service-commons/build.gradle
index 7f0aed937ba0ec96b2b5507d1b8827d38b7681da..e7505d862a5b51a9912b71e606d6da474fe03ac7 100644
--- a/backend/rest-service-commons/build.gradle
+++ b/backend/rest-service-commons/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     implementation 'org.springframework:spring-web'
     implementation 'org.apache.tomcat.embed:tomcat-embed-core'
 
+    implementation project(":logging-commons")
     implementation project(':test-helper-commons-spring')
 
     compileOnly 'org.jetbrains:annotations:latest.release'
diff --git a/backend/rest-service-commons/src/main/java/de/eshg/rest/service/commons/filter/RequestLoggingFilter.java b/backend/rest-service-commons/src/main/java/de/eshg/rest/service/commons/filter/RequestLoggingFilter.java
index 0daa231772800d60f78b5780688a609595aef4f1..7813120dd72d0ab0d811248d530516d4771c4b5c 100644
--- a/backend/rest-service-commons/src/main/java/de/eshg/rest/service/commons/filter/RequestLoggingFilter.java
+++ b/backend/rest-service-commons/src/main/java/de/eshg/rest/service/commons/filter/RequestLoggingFilter.java
@@ -5,16 +5,19 @@
 
 package de.eshg.rest.service.commons.filter;
 
+import de.eshg.logging.LoggingConstants;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.Closeable;
 import java.io.IOException;
 import java.util.Objects;
 import java.util.stream.Collectors;
 import org.jetbrains.annotations.VisibleForTesting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
 import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.HttpStatus;
@@ -55,20 +58,32 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
       filterChain.doFilter(request, response);
       return;
     }
-    log.info(
-        "Starting to process {} {}{}",
-        request.getMethod(),
-        request.getRequestURI(),
-        maskedQueryString(request.getQueryString()));
-    try {
-      filterChain.doFilter(request, response);
-    } finally {
-      log.info(
-          "Processed {} {} with result {}",
-          request.getMethod(),
-          request.getRequestURI(),
-          HttpStatus.valueOf(response.getStatus()));
+    try (Closeable closeable = putCorrelationIdToMdc(request)) {
+      log.trace("{}", closeable);
+      try {
+        log.info(
+            "Starting to process {} {}{}",
+            request.getMethod(),
+            request.getRequestURI(),
+            maskedQueryString(request.getQueryString()));
+
+        filterChain.doFilter(request, response);
+      } finally {
+        log.info(
+            "Processed {} {} with result {}",
+            request.getMethod(),
+            request.getRequestURI(),
+            HttpStatus.valueOf(response.getStatus()));
+      }
+    }
+  }
+
+  private static Closeable putCorrelationIdToMdc(HttpServletRequest request) {
+    String correlationId = request.getHeader(LoggingConstants.CORRELATION_ID_HEADER);
+    if (correlationId == null) {
+      return () -> {};
     }
+    return MDC.putCloseable(LoggingConstants.CORRELATION_ID_MDC_KEY, correlationId);
   }
 
   @VisibleForTesting
diff --git a/backend/school-entry/openApi.yaml b/backend/school-entry/openApi.yaml
index 8d45e9164f13036959a8368c169347fb5aeae597..627b49a9582cd82f2ae34701b1c40d64e0802720 100644
--- a/backend/school-entry/openApi.yaml
+++ b/backend/school-entry/openApi.yaml
@@ -2686,6 +2686,14 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/direct-procedure-type-assignment-on-import:
+    post:
+      operationId: enableDirectProcedureTypeAssignmentOnImport
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/enabled-new-features/{featureToDisable}:
     delete:
       operationId: disableNewFeature
@@ -3413,8 +3421,6 @@ components:
           items:
             type: integer
             format: int32
-      required:
-      - siblingsBirthYears
     CitizenAnamnesis:
       type: object
       properties:
@@ -5203,9 +5209,12 @@ components:
     GetSchoolEntryConfigResponse:
       type: object
       properties:
+        isDirectProcedureTypeAssignmentOnImport:
+          type: boolean
         locationSelectionMode:
           $ref: "#/components/schemas/LocationSelectionMode"
       required:
+      - isDirectProcedureTypeAssignmentOnImport
       - locationSelectionMode
     GetSchoolEntryFeatureTogglesResponse:
       type: object
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryConfigController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryConfigController.java
index b8b42a6a052653ba1e1e0ba18203123c25eed040..335a88dfa28a61b7f99795aba1e3195393312e61 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryConfigController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryConfigController.java
@@ -8,6 +8,7 @@ package de.eshg.schoolentry;
 import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.schoolentry.api.GetSchoolEntryConfigResponse;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -21,13 +22,19 @@ public class SchoolEntryConfigController {
   public static final String BASE_URL = BaseUrls.SchoolEntry.CONFIG_CONTROLLER;
 
   private final AppointmentBlockProperties appointmentBlockProperties;
+  private final SchoolEntryProperties schoolEntryProperties;
 
-  public SchoolEntryConfigController(AppointmentBlockProperties appointmentBlockProperties) {
+  public SchoolEntryConfigController(
+      AppointmentBlockProperties appointmentBlockProperties,
+      SchoolEntryProperties schoolEntryProperties) {
     this.appointmentBlockProperties = appointmentBlockProperties;
+    this.schoolEntryProperties = schoolEntryProperties;
   }
 
   @GetMapping
   public GetSchoolEntryConfigResponse getConfig() {
-    return new GetSchoolEntryConfigResponse(appointmentBlockProperties.getLocationSelectionMode());
+    return new GetSchoolEntryConfigResponse(
+        appointmentBlockProperties.getLocationSelectionMode(),
+        schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport());
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
index 3b871fed6f629cd4f796b13b9c4efe7c73b4312f..537d78756290375820204cf102bd0c2010f7a40d 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
@@ -19,6 +19,7 @@ import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.appointmentblock.api.GetFreeAppointmentsResponse;
 import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.procedure.domain.model.Pdf;
+import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.AnamnesisDto;
@@ -26,6 +27,7 @@ import de.eshg.schoolentry.business.model.ImportResult;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
 import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.*;
 import de.eshg.schoolentry.importer.ImportService;
 import de.eshg.schoolentry.importer.ImportType;
@@ -88,6 +90,7 @@ public class SchoolEntryController {
   private final Clock clock;
   private final SchoolEntryFeatureToggle featureToggle;
   private final AppointmentBlockProperties appointmentBlockProperties;
+  private final SchoolEntryProperties schoolEntryProperties;
 
   public SchoolEntryController(
       SchoolEntryService schoolEntryService,
@@ -99,7 +102,8 @@ public class SchoolEntryController {
       @Value("classpath:templates/import/CitizenListTemplate.xlsx") Resource citizenListTemplate,
       @Value("classpath:templates/import/SchoolListTemplate.xlsx") Resource schoolListTemplate,
       SchoolEntryFeatureToggle featureToggle,
-      AppointmentBlockProperties appointmentBlockProperties) {
+      AppointmentBlockProperties appointmentBlockProperties,
+      SchoolEntryProperties schoolEntryProperties) {
     this.schoolEntryService = schoolEntryService;
     this.importService = importService;
     this.medicalReportGenerator = medicalReportGenerator;
@@ -110,6 +114,7 @@ public class SchoolEntryController {
     this.clock = clock;
     this.featureToggle = featureToggle;
     this.appointmentBlockProperties = appointmentBlockProperties;
+    this.schoolEntryProperties = schoolEntryProperties;
   }
 
   @PostMapping
@@ -438,6 +443,10 @@ public class SchoolEntryController {
       @RequestParam(value = "schoolYear") @Min(1900) int schoolYear,
       @RequestPart("file") MultipartFile file)
       throws IOException {
+    if (schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport()) {
+      throw new BadRequestException(
+          "Citizen list import is not allowed when direct procedure type assignment is enabled.");
+    }
     return importData(file, ImportType.CITIZEN_LIST, null, null, Year.of(schoolYear));
   }
 
@@ -604,6 +613,7 @@ public class SchoolEntryController {
       @Valid @RequestBody CreateMedicalReportRequest request) {
     featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.MEDICAL_REPORT);
     SchoolEntryProcedure procedure = schoolEntryService.findProcedureByExternalId(procedureId);
+    Validator.validateProcedureStatusNotClosed(procedure);
     ProcedureDetailsData procedureDetailsData = schoolEntryService.augmentWithDetails(procedure);
 
     Pdf pdf = medicalReportGenerator.generateMedicalReport(procedureDetailsData.child(), request);
@@ -625,6 +635,7 @@ public class SchoolEntryController {
     featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_INFO_LETTER);
 
     SchoolEntryProcedure procedure = schoolEntryService.findProcedureByExternalId(procedureId);
+    Validator.validateProcedureStatusNotClosed(procedure);
     ProcedureDetailsData procedureDetailsData = schoolEntryService.augmentWithDetails(procedure);
 
     Pdf pdf =
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
index aea9d74c09ef26857ddbc8b88c9b88a99aab7f87..2c3f7d445bc170f3c62de39fb1a41821093be78e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
@@ -14,6 +14,7 @@ import static java.util.Comparator.nullsLast;
 
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.base.SortDirection;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.base.citizenuser.CitizenAccessCodeUserApi;
 import de.eshg.base.citizenuser.api.AddCitizenAccessCodeUserRequest;
 import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto;
@@ -53,19 +54,18 @@ import de.eshg.schoolentry.pdf.invitation.InvitationGenerator;
 import de.eshg.schoolentry.percentiles.PercentileCalculationService;
 import de.eshg.schoolentry.util.ExceptionUtil;
 import de.eshg.schoolentry.util.ProcedureSortKey;
+import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import de.eshg.schoolentry.util.ProgressEntryUtil;
 import de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType;
 import de.eshg.validation.ValidationUtil;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDate;
-import java.time.MonthDay;
 import java.time.Year;
 import java.util.*;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 import org.apache.commons.collections4.CollectionUtils;
-import org.jetbrains.annotations.VisibleForTesting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Sort;
@@ -105,6 +105,7 @@ public class SchoolEntryService {
   private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
   private final ProceduresHelper proceduresHelper;
   private final ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService;
+  private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
 
   public SchoolEntryService(
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
@@ -133,7 +134,8 @@ public class SchoolEntryService {
       ProcedureSearchService<SchoolEntryProcedure> procedureSearchService,
       SchoolEntryFeatureToggle schoolEntryFeatureToggle,
       ProceduresHelper proceduresHelper,
-      ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService) {
+      ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService,
+      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.personRepository = personRepository;
     this.hearingTestResultRepository = hearingTestResultRepository;
@@ -161,11 +163,14 @@ public class SchoolEntryService {
     this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
     this.proceduresHelper = proceduresHelper;
     this.procedureDeletionService = procedureDeletionService;
+    this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
   }
 
   public SchoolEntryProcedure createProcedure(CreateProcedureRequest request) {
     return createProcedures(
-            List.of(new ImportProcedureData(request.child(), request.type())),
+            List.of(
+                new ImportProcedureData(
+                    request.child(), ProcedureMapper.mapToDomain(request.type()))),
             null,
             null,
             null,
@@ -192,7 +197,7 @@ public class SchoolEntryService {
       ImportProcedureData procedure = procedures.get(i);
       ProcedureIds procedureIds = createdIds.get(i);
 
-      ProcedureTypeDto procedureType = procedure.procedureType();
+      ProcedureType procedureType = procedure.procedureType();
 
       SchoolEntryProcedure schoolEntryProcedure =
           saveSchoolEntryProcedure(
@@ -230,14 +235,14 @@ public class SchoolEntryService {
   private SchoolEntryProcedure saveSchoolEntryProcedure(
       UUID childIdFromCentralFile,
       List<UUID> custodianIdsFromCentralFile,
-      ProcedureTypeDto type,
+      ProcedureType type,
       UUID schoolId,
       UUID locationId,
       Year schoolYear,
       boolean isEntryLevel) {
     SchoolEntryProcedure schoolEntryProcedure = new SchoolEntryProcedure();
     schoolEntryProcedure.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
-    schoolEntryProcedure.setProcedureType(ProcedureMapper.mapToDomain(type));
+    schoolEntryProcedure.setProcedureType(type);
     schoolEntryProcedure.setSchoolId(schoolId);
     schoolEntryProcedure.setLocationId(locationId);
     schoolEntryProcedure.setEntryLevel(isEntryLevel);
@@ -1064,26 +1069,11 @@ public class SchoolEntryService {
     }
   }
 
-  public List<ProcedureWithChildData> searchOpenProceduresByChildWithExactMatching(
-      ImportChildData childData) {
-    List<SchoolEntryProcedure> procedures =
-        procedureSearchService
-            .searchProceduresByPerson(
-                childData.firstName(),
-                childData.lastName(),
-                childData.dateOfBirth(),
-                PersonType.PATIENT)
-            .stream()
-            .filter(procedure -> ProcedureStatus.OPEN.equals(procedure.getProcedureStatus()))
-            .toList();
-    return personClient
-        .augmentWithChildData(procedures)
-        .filter(
-            procedure ->
-                Objects.equals(procedure.child().firstName(), childData.firstName())
-                    && Objects.equals(procedure.child().lastName(), childData.lastName())
-                    && Objects.equals(procedure.child().dateOfBirth(), childData.dateOfBirth()))
-        .toList();
+  public Map<PersonKeyAttributes, List<ProcedureWithChildData>> searchForMergeCandidates(
+      Set<PersonKeyAttributes> searchAttributes) {
+    Map<PersonKeyAttributes, List<SchoolEntryProcedure>> proceduresByPersons =
+        procedureSearchService.searchOpenProceduresByPersons(searchAttributes, PersonType.PATIENT);
+    return personClient.augmentWithChildData(proceduresByPersons);
   }
 
   void updateHearingTestResult(
@@ -1456,37 +1446,11 @@ public class SchoolEntryService {
   }
 
   private void updateProcedureTypeWithSuggestion(SchoolEntryProcedure procedure) {
-    if (procedure.isEntryLevel()) {
-      procedure.setProcedureType(ProcedureType.ENTRY_LEVEL);
-    } else {
-      LocalDate dateOfBirth = personClient.fetchChildData(procedure).dateOfBirth();
-      if (isRegularSchoolEntry(dateOfBirth, procedure.getSchoolYear())) {
-        procedure.setProcedureType(ProcedureType.REGULAR_EXAMINATION);
-      } else {
-        procedure.setProcedureType(ProcedureType.CAN_CHILD);
-      }
-    }
-  }
-
-  @VisibleForTesting
-  boolean isRegularSchoolEntry(LocalDate dateOfBirth, Year schoolYear) {
-    MonthDay maxDateOfBirthForRegularSchoolEntry =
-        schoolEntryProperties.getMaxDateOfBirthForRegularSchoolEntry();
-    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      LocalDate maxDateOfBirthForRegularSchoolEntryWithYear =
-          schoolYear.minusYears(6).atMonthDay(maxDateOfBirthForRegularSchoolEntry);
-      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
-        return !dateOfBirth.isAfter(maxDateOfBirthForRegularSchoolEntryWithYear);
-      } else {
-        return dateOfBirth.isBefore(maxDateOfBirthForRegularSchoolEntryWithYear);
-      }
-    } else {
-      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
-        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) <= 0;
-      } else {
-        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) < 0;
-      }
-    }
+    procedure.setProcedureType(
+        procedureTypeAssignmentHelper.suggestProcedureType(
+            procedure.isEntryLevel(),
+            personClient.fetchChildData(procedure).dateOfBirth(),
+            procedure.getSchoolYear()));
   }
 
   void updateWaitingRoomDetails(
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetSchoolEntryConfigResponse.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetSchoolEntryConfigResponse.java
index bc81e1426ad914cf50ebbc038bdba951e733ffdd..a62f296077cc9302b60ef186e5f65c959b4898ef 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetSchoolEntryConfigResponse.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetSchoolEntryConfigResponse.java
@@ -8,4 +8,6 @@ package de.eshg.schoolentry.api;
 import de.eshg.lib.appointmentblock.LocationSelectionMode;
 import jakarta.validation.constraints.NotNull;
 
-public record GetSchoolEntryConfigResponse(@NotNull LocationSelectionMode locationSelectionMode) {}
+public record GetSchoolEntryConfigResponse(
+    @NotNull LocationSelectionMode locationSelectionMode,
+    @NotNull boolean isDirectProcedureTypeAssignmentOnImport) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/CitizenAdditionalChildInfoDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/CitizenAdditionalChildInfoDto.java
index 27ba79eee085e3e5cc7c6f21b94a9bb0ef737de1..6c10e56a9ac8383479efaa7699dc54c80f93044e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/CitizenAdditionalChildInfoDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/CitizenAdditionalChildInfoDto.java
@@ -6,13 +6,12 @@
 package de.eshg.schoolentry.api.anamnesis;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
 import java.util.List;
 
 @Schema(name = "CitizenAdditionalChildInfo")
 public record CitizenAdditionalChildInfoDto(
-    String responsiblePhysician, @NotNull List<Integer> siblingsBirthYears) {
+    String responsiblePhysician, List<Integer> siblingsBirthYears) {
   public CitizenAdditionalChildInfoDto() {
-    this(null, List.of());
+    this(null, null);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportProcedureData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportProcedureData.java
index 04000885613314d874fd4b6d50f35b2e9972f0ab..3c016e6188551a172572984f5e22212045f1d176 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportProcedureData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportProcedureData.java
@@ -5,24 +5,24 @@
 
 package de.eshg.schoolentry.business.model;
 
+import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.api.CreatePersonDto;
-import de.eshg.schoolentry.api.ProcedureTypeDto;
 import java.util.List;
 
 public record ImportProcedureData(
     CreatePersonDto child,
     List<ImportCustodianData> custodians,
-    ProcedureTypeDto procedureType,
+    ProcedureType procedureType,
     boolean isEntryLevel,
     boolean isEarlyExamination) {
 
-  public ImportProcedureData(CreatePersonDto child, ProcedureTypeDto procedureType) {
+  public ImportProcedureData(CreatePersonDto child, ProcedureType procedureType) {
     this(child, procedureType, false, false);
   }
 
   public ImportProcedureData(
       CreatePersonDto child,
-      ProcedureTypeDto procedureType,
+      ProcedureType procedureType,
       boolean isEntryLevel,
       boolean isEarlyExamination) {
     this(child, List.of(), procedureType, isEntryLevel, isEarlyExamination);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
index 082d6553d9d2a1c1fdc3a0c48e205485aeeb7400..75fbb4fbd86b680f9eb40df59f81f2293d3119fd 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
@@ -27,6 +27,7 @@ import de.eshg.schoolentry.domain.model.Person;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.mapper.PersonMapper;
 import java.util.*;
+import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -215,6 +216,34 @@ public class PersonClient {
     return new ProcedureWithPersonDetailsData(procedure, childDetailsData, custodianData);
   }
 
+  public Map<PersonKeyAttributes, List<ProcedureWithChildData>> augmentWithChildData(
+      Map<PersonKeyAttributes, List<SchoolEntryProcedure>> proceduresByPerson) {
+    if (proceduresByPerson.isEmpty()) {
+      return Map.of();
+    }
+
+    List<UUID> personIdsToFetch =
+        proceduresByPerson.values().stream()
+            .flatMap(Collection::stream)
+            .map(SchoolEntryProcedure::getChildIdFromCentralFile)
+            .toList();
+
+    Map<UUID, AddPersonFileStateResponse> personsById =
+        fetchPersonsBulk(personIdsToFetch, null, null, null, null).stream()
+            .collect(StreamUtil.toLinkedHashMap(AddPersonFileStateResponse::id));
+
+    Map<PersonKeyAttributes, List<ProcedureWithChildData>> result = new LinkedHashMap<>();
+    for (Entry<PersonKeyAttributes, List<SchoolEntryProcedure>> entry :
+        proceduresByPerson.entrySet()) {
+      List<ProcedureWithChildData> augmentedProcedures =
+          entry.getValue().stream()
+              .map(procedure -> extractChildData(procedure, personsById))
+              .toList();
+      result.put(entry.getKey(), augmentedProcedures);
+    }
+    return result;
+  }
+
   public Stream<ProcedureWithChildData> augmentWithChildData(
       List<SchoolEntryProcedure> procedures) {
     if (procedures.isEmpty()) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
index dfc7e859115e4ab5d8cce6ed5459e8788b1ac939..a876f9589426a3b03e7618493ac392c0ce8ba8a7 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
@@ -22,6 +22,7 @@ public final class SchoolEntryProperties implements ResettableProperties {
   private @NotNull MonthDay maxDateOfBirthForRegularSchoolEntry;
   private boolean maxDateOfBirthForRegularSchoolEntryIsInclusive;
   private @NotNull Integer maxNumberOfImportRows = 10_000;
+  private boolean directProcedureTypeAssignmentOnImport;
 
   public Period getBulkCreateAppointmentsMinLeadTime() {
     return bulkCreateAppointmentsMinLeadTime;
@@ -65,6 +66,15 @@ public final class SchoolEntryProperties implements ResettableProperties {
     this.bulkCreateAppointmentsMinLeadTime = bulkCreateAppointmentsMinLeadTime;
   }
 
+  public boolean isDirectProcedureTypeAssignmentOnImport() {
+    return directProcedureTypeAssignmentOnImport;
+  }
+
+  public void setDirectProcedureTypeAssignmentOnImport(
+      boolean directProcedureTypeAssignmentOnImport) {
+    this.directProcedureTypeAssignmentOnImport = directProcedureTypeAssignmentOnImport;
+  }
+
   public record Citizens(
       @NotNull Period freeAppointmentsMinLeadTime, @NotNull Period freeAppointmentsMaxLeadTime) {}
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java
index ed5335d8de59a559da3ddf0dc96821400a78b8a6..06786f0d11dd38fbe9eb099b94474405b904d630 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java
@@ -7,7 +7,7 @@ package de.eshg.schoolentry.importer;
 
 import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
-import de.eshg.schoolentry.api.ProcedureTypeDto;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.business.model.AddressData;
 import de.eshg.schoolentry.business.model.ImportChildData;
 import de.eshg.schoolentry.business.model.ImportCustodianData;
@@ -56,7 +56,7 @@ public class CitizenListRowProcessor extends RowProcessor<CitizenListRowValues>
     return new ImportProcedureData(
         PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
         values.getCustodians(),
-        ProcedureTypeDto.DRAFT_CITIZEN_OFFICE_IMPORT,
+        ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT,
         false,
         false);
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
index 01947dd5dbe8a1c548560caed4681cec34a44798..3c3af4faba5406b74b0e57f64540c612f2447849 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
@@ -13,17 +13,21 @@ import de.eshg.base.GenderDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.address.PostboxAddressDto;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.api.ImportStatisticsDto;
 import de.eshg.schoolentry.business.model.*;
 import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.time.Year;
 import java.util.*;
+import java.util.Map.Entry;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.*;
@@ -35,6 +39,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
 
 @Service
 public class ImportService {
@@ -46,11 +51,18 @@ public class ImportService {
 
   private final SchoolEntryService schoolEntryService;
   private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
+  private final SchoolEntryProperties schoolEntryProperties;
+  private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
 
   public ImportService(
-      SchoolEntryService schoolEntryService, SchoolEntryFeatureToggle schoolEntryFeatureToggle) {
+      SchoolEntryService schoolEntryService,
+      SchoolEntryFeatureToggle schoolEntryFeatureToggle,
+      SchoolEntryProperties schoolEntryProperties,
+      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
     this.schoolEntryService = schoolEntryService;
     this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
+    this.schoolEntryProperties = schoolEntryProperties;
+    this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
   }
 
   public ImportResult processSheetAndPersistProcedures(
@@ -62,7 +74,9 @@ public class ImportService {
       RowProcessor<? extends RowValues> rowProcessor =
           switch (importType) {
             case CITIZEN_LIST -> new CitizenListRowProcessor(normalizedSheet);
-            case SCHOOL_LIST -> new SchoolListRowProcessor(normalizedSheet);
+            case SCHOOL_LIST ->
+                new SchoolListRowProcessor(
+                    normalizedSheet, schoolYear, procedureTypeAssignmentHelper);
           };
       return new Importer<>(
               normalizedSheet, importType, rowProcessor, schoolId, locationId, schoolYear)
@@ -211,39 +225,59 @@ public class ImportService {
               .filter(Objects::nonNull)
               .toList();
       List<UUID> existingProcedureIds = schoolEntryService.collectExistingProcedures(procedureIds);
+      Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates;
+      if (schoolEntryFeatureToggle.isNewFeatureEnabled(
+          SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)) {
+        Set<PersonKeyAttributes> rowsToSearchFor =
+            rowValues.values().stream()
+                .filter(row -> row.getProcedureId() == null)
+                .filter(RowValues::isValid)
+                .map(Importer::getChildKeyAttributes)
+                .collect(StreamUtil.toLinkedHashSet());
+        mergeCandidates = schoolEntryService.searchForMergeCandidates(rowsToSearchFor);
+      } else {
+        mergeCandidates = Map.of();
+      }
+
+      for (Entry<Row, T> entry : rowValues.entrySet()) {
+        Row row = entry.getKey();
+        T value = entry.getValue();
+        if (value.getProcedureId() != null) {
+          if (existingProcedureIds.contains(value.getProcedureId())) {
+            writeStatus(row, IMPORTED_PREVIOUSLY);
+            stats.countPreviouslyImported();
+          } else {
+            writeStatus(row, INVALID_PROCEDURE_ID);
+            stats.countFailed();
+          }
+        } else if (value.getStatus() == DUPLICATE_WITHIN_LIST
+            || containsMatchingRow(validRows, value)) {
+          writeStatus(row, DUPLICATE_WITHIN_LIST);
+          stats.countDuplicated();
+        } else if (value.isValid()) {
+          if (schoolEntryFeatureToggle.isNewFeatureEnabled(
+              SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)) {
+            evaluateActionsWhenMergeIsEnabled(row, value, mergeCandidates);
+          } else {
+            validRows.importableRows().add(value);
+            stats.countCreated();
+          }
+        } else {
+          writeStatus(row, ERROR_INPUT_DATA);
+          stats.countFailed();
+        }
+      }
+    }
+
+    private static PersonKeyAttributes getChildKeyAttributes(RowValues rowValues) {
+      ImportChildData child = rowValues.getChild();
+      return new PersonKeyAttributes(child.firstName(), child.lastName(), child.dateOfBirth());
+    }
 
-      rowValues.forEach(
-          (row, value) -> {
-            if (value.getProcedureId() != null) {
-              if (existingProcedureIds.contains(value.getProcedureId())) {
-                writeStatus(row, IMPORTED_PREVIOUSLY);
-                stats.countPreviouslyImported();
-              } else {
-                writeStatus(row, INVALID_PROCEDURE_ID);
-                stats.countFailed();
-              }
-            } else if (value.getStatus() == DUPLICATE_WITHIN_LIST
-                || containsMatchingRow(validRows, value)) {
-              writeStatus(row, DUPLICATE_WITHIN_LIST);
-              stats.countDuplicated();
-            } else if (value.isValid()) {
-              if (schoolEntryFeatureToggle.isNewFeatureEnabled(
-                  SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)) {
-                merge(row, value);
-              } else {
-                validRows.importableRows().add(value);
-                stats.countCreated();
-              }
-            } else {
-              writeStatus(row, ERROR_INPUT_DATA);
-              stats.countFailed();
-            }
-          });
-    }
-
-    private void merge(Row row, T value) {
+    private void evaluateActionsWhenMergeIsEnabled(
+        Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
       List<ProcedureWithChildData> procedures =
-          schoolEntryService.searchOpenProceduresByChildWithExactMatching(value.getChild());
+          mergeCandidates.getOrDefault(getChildKeyAttributes(value), List.of());
       if (procedures.isEmpty()) {
         validRows.importableRows().add(value);
         stats.countCreated();
@@ -253,6 +287,9 @@ public class ImportService {
             row, DUPLICATE_IN_ASSET, procedures.getFirst().procedure().getExternalId());
         stats.countMergeFailed();
       } else {
+        Assert.isTrue(
+            !schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport(),
+            "Procedures of a draft type should not exist when direct procedure type assignment is enabled.");
         ProcedureWithChildData procedure = procedures.getFirst();
         if (procedureMatchesImportValues(procedure, value)) {
           value.setProcedureId(procedure.procedure().getExternalId());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java
index a7c5cab9b32582b96a543c386e010af1c7c07507..6686702eaa69d30e09ceb7f582838a4e1dc05d9a 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java
@@ -8,12 +8,14 @@ package de.eshg.schoolentry.importer;
 import static de.eshg.schoolentry.importer.SchoolListRowProcessor.SchoolListFields.*;
 
 import de.eshg.base.CountryCodeDto;
-import de.eshg.schoolentry.api.ProcedureTypeDto;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.business.model.AddressData;
 import de.eshg.schoolentry.business.model.ImportChildData;
 import de.eshg.schoolentry.business.model.ImportProcedureData;
 import de.eshg.schoolentry.business.model.MergeProcedureData;
 import de.eshg.schoolentry.mapper.PersonMapper;
+import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
+import java.time.Year;
 import java.util.Objects;
 import java.util.function.BiConsumer;
 import org.apache.poi.ss.usermodel.Cell;
@@ -44,8 +46,14 @@ public class SchoolListRowProcessor extends RowProcessor<SchoolListRowValues> {
     }
   }
 
-  public SchoolListRowProcessor(Sheet sheet) {
+  private final Year schoolYear;
+  private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+
+  public SchoolListRowProcessor(
+      Sheet sheet, Year schoolYear, ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
     super(sheet);
+    this.schoolYear = schoolYear;
+    this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
   }
 
   @Override
@@ -69,9 +77,12 @@ public class SchoolListRowProcessor extends RowProcessor<SchoolListRowValues> {
 
   @Override
   public ImportProcedureData mapValuesToImportData(SchoolListRowValues values) {
+    ProcedureType procedureType =
+        procedureTypeAssignmentHelper.getProcedureTypeForSchoolListImport(
+            values.isEntryLevel(), values.getChild().dateOfBirth(), schoolYear);
     return new ImportProcedureData(
         PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
-        ProcedureTypeDto.DRAFT_SCHOOL_IMPORT,
+        procedureType,
         values.isEntryLevel(),
         values.isEarlyExamination());
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
index 55b88c0313153676aa5380c3d8567fbeee8479e6..1bbbccd89872162092025fa51f476f31c0e58c79 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
@@ -222,11 +222,12 @@ public class AnamnesisMapper {
     anamnesis.setResponsiblePhysician(
         citizenAnamnesisDto.additionalChildInfo().responsiblePhysician());
     anamnesis.setNumberOfSiblings(
-        citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears().isEmpty()
+        citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears() == null
+                || citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears().isEmpty()
             ? null
             : citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears().size());
     anamnesis.setSiblingsBirthYears(
-        citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears().isEmpty()
+        citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears() == null
             ? null
             : citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears());
 
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/population/CreateLabelsTask.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/population/CreateLabelsTask.java
index dcdfb9344693fd1776dbd4b538370e30e5996f80..a8d177a879c94a6da41375dae072461b05b9e1f2 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/population/CreateLabelsTask.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/population/CreateLabelsTask.java
@@ -16,13 +16,18 @@ import org.springframework.stereotype.Component;
 public class CreateLabelsTask {
 
   public static final String SPECIAL_NEEDS_LABEL_NAME = "Besonderer Förderbedarf";
+  public static final String INFORMATION_BLOCK_LABEL_NAME = "Auskunftssperre";
 
   private static final List<LabelData> SYSTEM_LABELS =
       List.of(
           new LabelData(
               SPECIAL_NEEDS_LABEL_NAME,
               "Vorgänge mit dieser Kennung erhalten Termine aus den geplanten Terminblöcken der Art \"Besonderer Förderbedarf\"",
-              "#008000"));
+              "#008000"),
+          new LabelData(
+              INFORMATION_BLOCK_LABEL_NAME,
+              "Für den Vorgang liegt eine Auskunftssperre vor.",
+              "#800080"));
 
   private final LabelRepository labelRepository;
   private final TransactionHelper transactionHelper;
@@ -37,7 +42,7 @@ public class CreateLabelsTask {
     transactionHelper.executeInTransaction(
         () ->
             SYSTEM_LABELS.forEach(
-                (labelData) -> {
+                labelData -> {
                   if (!labelRepository.existsByName(labelData.name())) {
                     Label label = new Label();
                     label.setName(labelData.name());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
index d6bcdb129e8040639460d780b25008791d85338a..6bddc6498e135f89dc227e31f7f2361a1a12a4c4 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
@@ -16,6 +16,7 @@ import de.eshg.schoolentry.api.SchoolEntryAppointmentBlockPopulationResult;
 import de.eshg.schoolentry.api.SchoolEntryProcedurePopulationResult;
 import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
 import de.eshg.testhelper.TestHelperController;
 import de.eshg.testhelper.api.PopulationRequest;
@@ -38,6 +39,7 @@ public class SchoolEntryTestHelperController extends TestHelperController
 
   private final SchoolEntryTestHelperService schoolEntryTestHelperService;
   private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
+  private final SchoolEntryProperties schoolEntryProperties;
   private final SchoolEntryProceduresPopulator schoolEntryProceduresPopulator;
   private final AppointmentBlockGroupsPopulator schoolEntryAppointmentBlockGroupsPopulator;
   private final AuditLogTestHelperService auditLogTestHelperService;
@@ -46,6 +48,7 @@ public class SchoolEntryTestHelperController extends TestHelperController
   public SchoolEntryTestHelperController(
       SchoolEntryTestHelperService schoolEntryTestHelperService,
       SchoolEntryFeatureToggle schoolEntryFeatureToggle,
+      SchoolEntryProperties schoolEntryProperties,
       SchoolEntryProceduresPopulator schoolEntryProceduresPopulator,
       AppointmentBlockGroupsPopulator schoolEntryAppointmentBlockGroupsPopulator,
       AuditLogTestHelperService auditLogTestHelperService,
@@ -53,6 +56,7 @@ public class SchoolEntryTestHelperController extends TestHelperController
     super(schoolEntryTestHelperService);
     this.schoolEntryTestHelperService = schoolEntryTestHelperService;
     this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
+    this.schoolEntryProperties = schoolEntryProperties;
     this.schoolEntryProceduresPopulator = schoolEntryProceduresPopulator;
     this.schoolEntryAppointmentBlockGroupsPopulator = schoolEntryAppointmentBlockGroupsPopulator;
     this.auditLogTestHelperService = auditLogTestHelperService;
@@ -89,6 +93,11 @@ public class SchoolEntryTestHelperController extends TestHelperController
     appointmentBlockProperties.setLocationSelectionMode(newLocationSelectionMode);
   }
 
+  @PostExchange("/direct-procedure-type-assignment-on-import")
+  public void enableDirectProcedureTypeAssignmentOnImport() {
+    schoolEntryProperties.setDirectProcedureTypeAssignmentOnImport(true);
+  }
+
   @PostExchange("/population/procedures")
   public SchoolEntryProcedurePopulationResult populateProcedures(
       @Valid @RequestBody PopulationRequest request) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a11b1d9a23913a31ee8d730c34fdb62b0f5b305d
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.util;
+
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.schoolentry.config.SchoolEntryFeature;
+import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
+import java.time.LocalDate;
+import java.time.MonthDay;
+import java.time.Year;
+import org.jetbrains.annotations.VisibleForTesting;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ProcedureTypeAssignmentHelper {
+
+  private final SchoolEntryProperties schoolEntryProperties;
+  private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
+
+  public ProcedureTypeAssignmentHelper(
+      SchoolEntryProperties schoolEntryProperties,
+      SchoolEntryFeatureToggle schoolEntryFeatureToggle) {
+    this.schoolEntryProperties = schoolEntryProperties;
+    this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
+  }
+
+  public ProcedureType getProcedureTypeForSchoolListImport(
+      boolean isEntryLevel, LocalDate dateOfBirth, Year schoolYear) {
+    if (schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport()) {
+      return suggestProcedureType(isEntryLevel, dateOfBirth, schoolYear);
+    }
+    return ProcedureType.DRAFT_SCHOOL_IMPORT;
+  }
+
+  public ProcedureType suggestProcedureType(
+      boolean isEntryLevel, LocalDate dateOfBirth, Year schoolYear) {
+    if (isEntryLevel) {
+      return ProcedureType.ENTRY_LEVEL;
+    } else {
+      if (isRegularSchoolEntry(dateOfBirth, schoolYear)) {
+        return ProcedureType.REGULAR_EXAMINATION;
+      } else {
+        return ProcedureType.CAN_CHILD;
+      }
+    }
+  }
+
+  @VisibleForTesting
+  boolean isRegularSchoolEntry(LocalDate dateOfBirth, Year schoolYear) {
+    MonthDay maxDateOfBirthForRegularSchoolEntry =
+        schoolEntryProperties.getMaxDateOfBirthForRegularSchoolEntry();
+    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
+      LocalDate maxDateOfBirthForRegularSchoolEntryWithYear =
+          schoolYear.minusYears(6).atMonthDay(maxDateOfBirthForRegularSchoolEntry);
+      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
+        return !dateOfBirth.isAfter(maxDateOfBirthForRegularSchoolEntryWithYear);
+      } else {
+        return dateOfBirth.isBefore(maxDateOfBirthForRegularSchoolEntryWithYear);
+      }
+    } else {
+      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
+        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) <= 0;
+      } else {
+        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) < 0;
+      }
+    }
+  }
+}
diff --git a/backend/service-commons/src/main/resources/logback-spring.xml b/backend/service-commons/src/main/resources/logback-spring.xml
index bb00d79cf3c2cc0a17ffa9828bd6aab7dca81770..31893156440a41e7104ea125296477bbcd83b2de 100644
--- a/backend/service-commons/src/main/resources/logback-spring.xml
+++ b/backend/service-commons/src/main/resources/logback-spring.xml
@@ -7,7 +7,7 @@
   <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
   <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
 
-  <springProfile name="!(dev | production)">
+  <springProfile name="local">
     <root level="info">
       <appender-ref ref="CONSOLE" />
     </root>
@@ -24,8 +24,6 @@
   </springProfile>
 
   <springProfile name="production">
-<!--    <property name="DEBUG_LOG_DIRECTORY" value="${DEBUG_LOG_DIRECTORY:-/logs}"/>-->
-
     <appender name="logstash-info" class="ch.qos.logback.core.ConsoleAppender">
       <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
       <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
@@ -33,24 +31,25 @@
       </filter>
     </appender>
 
-    <!-- TODO: clarify the volume path -->
-<!--    <appender name="debug-log" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
-<!--      <file>${DEBUG_LOG_DIRECTORY}/debug.log</file>-->
+    <property name="DEBUG_LOG_DIRECTORY" value="/var/log/eshg-debug"/>
+
+    <appender name="debug-log" class="ch.qos.logback.core.rolling.RollingFileAppender">
+      <file>${DEBUG_LOG_DIRECTORY}/debug.log</file>
 
-<!--      <encoder>-->
-<!--        <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%-5p] [%t] %-40.40logger{39} : %m%n%wEx</pattern>-->
-<!--        <charset>UTF-8</charset>-->
-<!--      </encoder>-->
+      <encoder>
+        <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} [%-5p] [%t] [%X] %-40.40logger{39} : %m%n%wEx</pattern>
+        <charset>UTF-8</charset>
+      </encoder>
 
-<!--      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
-<!--        <fileNamePattern>${DEBUG_LOG_DIRECTORY}/debug.log.%d{yyyy-MM-dd}</fileNamePattern>-->
-<!--        <maxHistory>14</maxHistory>-->
-<!--      </rollingPolicy>-->
-<!--    </appender>-->
+      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <fileNamePattern>${DEBUG_LOG_DIRECTORY}/debug.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
+        <maxHistory>14</maxHistory>
+      </rollingPolicy>
+    </appender>
 
     <root level="info">
       <appender-ref ref="logstash-info" />
-<!--      <appender-ref ref="debug-log" />-->
+      <appender-ref ref="debug-log" />
     </root>
   </springProfile>
 
diff --git a/backend/service-directory/src/main/java/de/eshg/servicedirectory/ServicedirectoryApplication.java b/backend/service-directory/src/main/java/de/eshg/servicedirectory/ServiceDirectoryApplication.java
similarity index 75%
rename from backend/service-directory/src/main/java/de/eshg/servicedirectory/ServicedirectoryApplication.java
rename to backend/service-directory/src/main/java/de/eshg/servicedirectory/ServiceDirectoryApplication.java
index 666e481410199b659c388c6cf92730bddfb2ab2f..ccd78264fda13d3678ada37e61e15d0cfc347a19 100644
--- a/backend/service-directory/src/main/java/de/eshg/servicedirectory/ServicedirectoryApplication.java
+++ b/backend/service-directory/src/main/java/de/eshg/servicedirectory/ServiceDirectoryApplication.java
@@ -9,9 +9,9 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 @SpringBootApplication
-public class ServicedirectoryApplication {
+public class ServiceDirectoryApplication {
 
   public static void main(String[] args) {
-    SpringApplication.run(ServicedirectoryApplication.class, args);
+    SpringApplication.run(ServiceDirectoryApplication.class, args);
   }
 }
diff --git a/backend/settings.gradle b/backend/settings.gradle
index bd66948600411fc15eec2c127edbbf65c9a24284..a05e9f7f4183309201bc368e5c8418ae34ae9205 100644
--- a/backend/settings.gradle
+++ b/backend/settings.gradle
@@ -82,6 +82,7 @@ include 'lib-service-directory-api'
 include 'lib-statistics'
 include 'lib-statistics-api'
 include 'local-service-directory'
+include 'logging-commons'
 include 'measles-protection'
 include 'opendata'
 include 'relay-server'
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/client/HttpProxyClient.java b/backend/spatz/src/main/java/de/eshg/spatz/client/HttpProxyClient.java
index ccd22878d90f370626d66e2980045be6bddc5a09..6db974aafa345a2eb42bbcaa31ce638ef9fca68a 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/client/HttpProxyClient.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/client/HttpProxyClient.java
@@ -70,7 +70,7 @@ public class HttpProxyClient {
       HttpMethod method,
       Consumer<? super HttpHeaders> headersConsumer,
       HttpClient httpClient) {
-    log.info("Starting proxy client");
+    log.debug("Starting proxy client");
 
     logConnecting(uri, httpClient);
 
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/common/ServiceDirectoryTopologyService.java b/backend/spatz/src/main/java/de/eshg/spatz/common/ServiceDirectoryTopologyService.java
index 15fcd5e06c5d53068fe13bbd9ab4f9b70b468220..90a4659b5bc6d00449de8d8c63fef10a8631b221 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/common/ServiceDirectoryTopologyService.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/common/ServiceDirectoryTopologyService.java
@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.event.Level;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -88,15 +89,16 @@ public class ServiceDirectoryTopologyService {
 
       if (eTag.equals(lastEtag)) {
         logger.debug("serviceDirectory topology has not changed");
+        logTrustedActors(Level.TRACE);
       } else {
         trustedActors = getValidTrustedActors(trustedActorsCacheEntry);
 
         logTopologyChanged(trustedActorsCacheEntry, trustedActors);
+        logTrustedActors(Level.INFO);
         notifyListeners(trustedActors);
         lastEtag = eTag;
       }
 
-      logTrustedActors();
       lastSuccessfulPollTime = Instant.now();
     } catch (RuntimeException e) {
       handlePollingFailure(e);
@@ -155,15 +157,17 @@ public class ServiceDirectoryTopologyService {
         trustedActorsCacheEntry.eTag());
   }
 
-  private void logTrustedActors() {
-    if (logger.isTraceEnabled()) {
-      logger.trace(
-          "valid active inbound actors: {}",
-          trustedActors.inbound.stream().map(ActorResponseDto::commonName).sorted().toList());
-      logger.trace(
-          "valid active outbound actors: {}",
-          trustedActors.outbound.stream().map(ActorResponseDto::commonName).sorted().toList());
-    }
+  private void logTrustedActors(Level level) {
+    logger
+        .atLevel(level)
+        .log(
+            "valid active inbound actors: {}",
+            trustedActors.inbound.stream().map(ActorResponseDto::commonName).sorted().toList());
+    logger
+        .atLevel(level)
+        .log(
+            "valid active outbound actors: {}",
+            trustedActors.outbound.stream().map(ActorResponseDto::commonName).sorted().toList());
   }
 
   public interface TopologyChangedListener {
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/config/SpatzConfigurationProperties.java b/backend/spatz/src/main/java/de/eshg/spatz/config/SpatzConfigurationProperties.java
index 93a4bbe1d57aabae7962050bd515756d2abb0883..ebf1f1441926c72671c274fc25e102e4f486843b 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/config/SpatzConfigurationProperties.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/config/SpatzConfigurationProperties.java
@@ -55,13 +55,16 @@ public record SpatzConfigurationProperties(
    *     connection and all content then will be forwarded to the application container.
    * @param targetPort Port where mTLS-terminated data ist forwarded to unencrypted
    * @param forceClientAuth can be set to {@code false} to disable mTLS in favor of TLS
+   * @param clientCnAllowList List of CNs that are allowed to call this service in addition to CNs
+   *     configured in the service-directory
    */
   public record InboundConfiguration(
       String listeningHost,
       int handlerPort,
       String targetHost,
       int targetPort,
-      boolean forceClientAuth) {}
+      boolean forceClientAuth,
+      List<String> clientCnAllowList) {}
 
   /**
    * @param handlerPort Port for outgoing unencrypted traffic.
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/dns/DnsResolver.java b/backend/spatz/src/main/java/de/eshg/spatz/dns/DnsResolver.java
index 4a00c83e846dea5c1236d3d5fa96e9d781a14141..54437497944ed8926f2d64d9c259564264d05260 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/dns/DnsResolver.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/dns/DnsResolver.java
@@ -191,7 +191,7 @@ public class DnsResolver {
       Message request, Resolver resolver, List<String> forwardRequestAllowList) throws IOException {
     String name = request.getQuestion().getName().toString(true).toLowerCase();
     if (!forwardRequestAllowList.contains(name)) {
-      logger.warn("Refusing to forward query not in allow-list");
+      logger.warn("Refusing to forward query to {} not in allow-list", name);
       return error(request, Rcode.REFUSED);
     }
     logger.trace("Forwarding request to {}", resolver);
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/relay/RelayConnector.java b/backend/spatz/src/main/java/de/eshg/spatz/relay/RelayConnector.java
index 7989f8116e210564f05ee51504aaa1c92148e526..f02e1a6a7ffc871f865907b620b87127b746bfe5 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/relay/RelayConnector.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/relay/RelayConnector.java
@@ -220,7 +220,7 @@ public class RelayConnector extends WebSocketClient {
       return;
     }
     String id = UUID.randomUUID().toString();
-    logger.info("Sending ping {}", id);
+    logger.debug("Sending ping {}", id);
     try {
       PingFrame ping = new PingFrame();
       ping.setPayload(ByteBuffer.wrap(id.getBytes(StandardCharsets.UTF_8)));
@@ -322,7 +322,7 @@ public class RelayConnector extends WebSocketClient {
 
   @Override
   public void onMessage(String message) {
-    logger.info("Received String message: {}", message);
+    logger.debug("Received String message: {}", message);
     onMessage(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)));
   }
 
@@ -425,7 +425,7 @@ public class RelayConnector extends WebSocketClient {
 
   @Override
   public void onWebsocketPong(WebSocket conn, Framedata f) {
-    logger.info("Received pong {}", f);
+    logger.debug("Received pong {}", f);
     String pongPayload = StandardCharsets.UTF_8.decode(f.getPayloadData()).toString();
     if (!pongPayload.equals(outstandingPingPayload.get())) {
       logger.warn("Payload mismatch: expected {}. Ignoring pong", outstandingPingPayload.get());
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/security/CertificateValidationService.java b/backend/spatz/src/main/java/de/eshg/spatz/security/CertificateValidationService.java
index 51030e9d28ab60fe5fe4a4886821a5a70f783979..55d4f16900463c27a0f3384f78b0102a369a140a 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/security/CertificateValidationService.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/security/CertificateValidationService.java
@@ -98,6 +98,8 @@ public class CertificateValidationService {
     for (ActorResponseDto actor : allActors) {
       if (ActorTypeDto.LSD == actor.type() || isValidActor(actor)) {
         result.add(actor);
+      } else {
+        log.info("Ignoring invalid actor {}", actor.commonName());
       }
     }
 
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/server/ProxyServer.java b/backend/spatz/src/main/java/de/eshg/spatz/server/ProxyServer.java
index 8c84ba073928adee0c4dce218db8d3270bba9539..096f6b30d205057a3c07e6b9f0e63a09cf477207 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/server/ProxyServer.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/server/ProxyServer.java
@@ -10,6 +10,7 @@ import static de.eshg.servicedirectory.util.X509Utils.ESHGACTOR_BUNDLE_NAME;
 import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.ssl.SslContext;
 import jakarta.annotation.PreDestroy;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.net.ssl.SSLException;
@@ -49,6 +50,7 @@ public abstract class ProxyServer implements HealthIndicator {
   private DisposableChannel server;
 
   private final boolean clientAuth;
+  private final List<String> clientCnAllowList;
   private final SslBundles sslBundles;
   private final SSLFactory dynamicSSLFactory;
 
@@ -62,11 +64,13 @@ public abstract class ProxyServer implements HealthIndicator {
       String listeningHost,
       Integer listeningPort,
       boolean clientAuth,
+      List<String> clientCnAllowList,
       SslBundles sslBundles) {
     this.baseServer = baseServer;
     this.listeningHost = Objects.requireNonNull(listeningHost);
     this.listeningPort = Objects.requireNonNull(listeningPort);
     this.clientAuth = clientAuth;
+    this.clientCnAllowList = clientCnAllowList;
     this.sslBundles = sslBundles;
     dynamicSSLFactory =
         SSLFactory.builder()
@@ -93,6 +97,10 @@ public abstract class ProxyServer implements HealthIndicator {
     return clientAuth;
   }
 
+  public List<String> getClientCnAllowList() {
+    return clientCnAllowList;
+  }
+
   void onSslBundleUpdate(SslBundle sslBundle) {
     logger.info("ssl configuration changed, applying for mTLS connections");
 
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/InboundServer.java b/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/InboundServer.java
index 93f94df479d512f8e1b1abe94d250cfda0995526..cdeb082810f96a3a287373434b939e06e2027b71 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/InboundServer.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/InboundServer.java
@@ -23,7 +23,9 @@ import io.netty.handler.ssl.SslHandler;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import org.reactivestreams.Publisher;
@@ -60,6 +62,7 @@ public class InboundServer extends ProxyServer implements TopologyChangedListene
         inboundConfiguration.listeningHost(),
         inboundConfiguration.handlerPort(),
         inboundConfiguration.forceClientAuth(),
+        Optional.ofNullable(inboundConfiguration.clientCnAllowList()).orElse(List.of()),
         sslBundles);
     this.inboundTargetPort = inboundConfiguration.targetPort();
     this.inboundTargetHost =
@@ -110,6 +113,11 @@ public class InboundServer extends ProxyServer implements TopologyChangedListene
                       EshgHttpHeaders.X_ESHG_SENDER_ORGUNIT.headerName,
                       senderActor.orgUnitId().toString()
                     });
+              } else if (!getClientCnAllowList().contains(peerCommonName)) {
+                throw new SslCertificateException(
+                    "Rejecting certificate with CN "
+                        + peerCommonName
+                        + " not in valid active inbound actors or client CN allow list");
               }
 
               ActorResponseDto currentSelf = self;
@@ -133,6 +141,7 @@ public class InboundServer extends ProxyServer implements TopologyChangedListene
 
               headersHolder.set(headers.toArray(String[][]::new));
             } catch (Exception e) {
+              if (e instanceof SslCertificateException) throw (SslCertificateException) e;
               throw new SslCertificateException("could not parse client certificate: " + e, e);
             }
           });
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/SslCertificateException.java b/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/SslCertificateException.java
index 492ad7de2373057045bd07e6843699f23ca7f4b0..77827749d3fa179d91a702165d4b728912b02e12 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/SslCertificateException.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/server/inbound/SslCertificateException.java
@@ -13,4 +13,8 @@ public class SslCertificateException extends RuntimeException {
   public SslCertificateException(String message, Throwable cause) {
     super(message, cause);
   }
+
+  public SslCertificateException(String message) {
+    super(message);
+  }
 }
diff --git a/backend/spatz/src/main/java/de/eshg/spatz/server/outbound/OutboundServer.java b/backend/spatz/src/main/java/de/eshg/spatz/server/outbound/OutboundServer.java
index 2937d8ec87aff43a6ef543e007d4cced3747464d..4cfa1f57c902e1b757eae3e279504529188b118f 100644
--- a/backend/spatz/src/main/java/de/eshg/spatz/server/outbound/OutboundServer.java
+++ b/backend/spatz/src/main/java/de/eshg/spatz/server/outbound/OutboundServer.java
@@ -28,6 +28,7 @@ import java.net.SocketAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 import javax.net.ssl.SSLHandshakeException;
@@ -67,6 +68,7 @@ public final class OutboundServer extends ProxyServer {
         outboundConfiguration.listeningHost(),
         outboundConfiguration.handlerPort(),
         true,
+        List.of(),
         sslBundles);
     this.outboundTargetPort = outboundConfiguration.targetPort();
     this.addressMapper = addressMapper;
diff --git a/backend/statistics/openApi.yaml b/backend/statistics/openApi.yaml
index cb4b6c1b2b0081ff65f6b0f091869c638dd590ea..b02368506f866299539d14d78b223dee0be8542e 100644
--- a/backend/statistics/openApi.yaml
+++ b/backend/statistics/openApi.yaml
@@ -649,7 +649,10 @@ paths:
         content:
           application/json:
             schema:
-              $ref: "#/components/schemas/UpdateReportSeriesRequest"
+              oneOf:
+              - $ref: "#/components/schemas/ActivateAutoReportSeriesRequest"
+              - $ref: "#/components/schemas/DeactivateAutoReportSeriesRequest"
+              - $ref: "#/components/schemas/UpdateNameAndDescriptionReportSeriesRequest"
         required: true
       responses:
         "200":
@@ -658,10 +661,25 @@ paths:
               schema:
                 $ref: "#/components/schemas/ReportSeries"
           description: The patched report series
-      summary: Change title and description of a report series
+      summary: Change title and description of a report series or change activation
       tags:
       - ReportSeries
   /statistic/report/{reportId}:
+    delete:
+      operationId: deleteReport
+      parameters:
+      - in: path
+        name: reportId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: Returned when the report is deleted
+      summary: Delete a report
+      tags:
+      - Report
     get:
       operationId: getReportDetailPage
       parameters:
@@ -978,6 +996,34 @@ components:
           type: string
       required:
       - '@type'
+    AbstractUpdateReportSeriesRequest:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+      required:
+      - '@type'
+    ActivateAutoReportSeriesRequest:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
+      - type: object
+        properties:
+          frequency:
+            $ref: "#/components/schemas/Frequency"
+          reportingPeriod:
+            $ref: "#/components/schemas/ReportingPeriod"
+          startMonth:
+            type: integer
+            format: int32
+            maximum: 12
+            minimum: 1
+      required:
+      - frequency
+      - reportingPeriod
+      - startMonth
     AddAutoReportSeriesRequest:
       type: object
       allOf:
@@ -1603,6 +1649,10 @@ components:
       required:
       - code
       - name
+    DeactivateAutoReportSeriesRequest:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
     DecimalAttribute:
       type: object
       allOf:
@@ -1966,7 +2016,7 @@ components:
           type: integer
           format: int64
           minimum: 0
-        userDto:
+        user:
           $ref: "#/components/schemas/User"
       required:
       - evaluations
@@ -2034,7 +2084,9 @@ components:
           type: integer
           format: int64
           minimum: 0
-        userDto:
+        userReport:
+          $ref: "#/components/schemas/User"
+        userReportSeries:
           $ref: "#/components/schemas/User"
       required:
       - createdAt
@@ -2893,13 +2945,16 @@ components:
           type: string
       required:
       - name
-    UpdateReportSeriesRequest:
+    UpdateNameAndDescriptionReportSeriesRequest:
       type: object
-      properties:
-        description:
-          type: string
-        name:
-          type: string
+      allOf:
+      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
+      - type: object
+        properties:
+          description:
+            type: string
+          name:
+            type: string
       required:
       - name
     User:
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
index 045a373f522cc41cba821bc838b777185a044449..066a83590fe2feea9537cdd34d4c7ed48ffc1fff 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
@@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.UUID;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.service.annotation.DeleteExchange;
 import org.springframework.web.service.annotation.GetExchange;
 import org.springframework.web.service.annotation.HttpExchange;
 
@@ -42,4 +43,12 @@ public class ReportController {
 
     return reportService.getReportDetailPage(reportId);
   }
+
+  @DeleteExchange(value = "/{reportId}", accept = APPLICATION_JSON_VALUE)
+  @ApiResponse(responseCode = "200", description = "Returned when the report is deleted")
+  @Operation(summary = "Delete a report")
+  public void deleteReport(@PathVariable(name = "reportId") UUID reportId) {
+    statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    reportService.deleteReport(reportId);
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
index bdfa6b8920f95e279a9387a946600940226ec5d4..bb485f562679b062a72575599f76b94a3855c546 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
@@ -9,11 +9,11 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
+import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
-import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
 import de.eshg.statistics.config.StatisticsFeature;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import io.swagger.v3.oas.annotations.Operation;
@@ -64,10 +64,10 @@ public class ReportSeriesController {
 
   @PatchExchange(value = "/{reportSeriesId}", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "The patched report series")
-  @Operation(summary = "Change title and description of a report series")
+  @Operation(summary = "Change title and description of a report series or change activation")
   public ReportSeriesDto updateReportSeries(
       @PathVariable(name = "reportSeriesId") UUID reportSeriesId,
-      @RequestBody @Valid UpdateReportSeriesRequest updateReportSeriesRequest) {
+      @RequestBody @Valid AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
     return reportSeriesService.updateReportSeries(reportSeriesId, updateReportSeriesRequest);
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
index f0f6d7c4c9594a8ecc70dc7b255a2121afc41db2..5370940f471f87e75101b003247722c7ccb0302d 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
@@ -12,12 +12,15 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
+import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
+import de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
+import de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
-import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
+import de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest;
 import de.eshg.statistics.mapper.ReportMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
@@ -43,14 +46,17 @@ import org.springframework.transaction.annotation.Transactional;
 public class ReportSeriesService {
   private final ReportSeriesRepository reportSeriesRepository;
   private final StatisticService statisticService;
+  private final ReportService reportService;
   private final Clock clock;
 
   public ReportSeriesService(
       ReportSeriesRepository reportSeriesRepository,
       StatisticService statisticService,
+      ReportService reportService,
       Clock clock) {
     this.reportSeriesRepository = reportSeriesRepository;
     this.statisticService = statisticService;
+    this.reportService = reportService;
     this.clock = clock;
   }
 
@@ -111,20 +117,26 @@ public class ReportSeriesService {
     reportSeries.setPeriod(
         ReportMapper.mapToReportingPeriod(addAutoReportSeriesRequest.reportingPeriod()));
 
-    LocalDate executionAndEndDate = calculateExecutionDate(addAutoReportSeriesRequest.startMonth());
+    addNewPlannedReportToSeries(
+        reportSeries, "1", addAutoReportSeriesRequest.startMonth(), statistic);
+
+    return reportSeries;
+  }
+
+  private void addNewPlannedReportToSeries(
+      ReportSeries reportSeries, String name, int startMonth, Statistic statistic) {
+    LocalDate executionAndEndDate = calculateExecutionDate(startMonth);
     LocalDate dateStart =
         ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
 
     reportSeries.addReport(
         ReportService.createReport(
-            "1",
+            name,
             dateStart.atStartOfDay(clock.getZone()).toInstant(),
             executionAndEndDate.atStartOfDay(clock.getZone()).toInstant(),
             AggregationResultState.PLANNED,
             executionAndEndDate,
             statistic));
-
-    return reportSeries;
   }
 
   private LocalDate calculateExecutionDate(int startMonth) {
@@ -150,19 +162,91 @@ public class ReportSeriesService {
 
   @Transactional
   public ReportSeriesDto updateReportSeries(
-      UUID reportSeriesId, UpdateReportSeriesRequest updateReportSeriesRequest) {
+      UUID reportSeriesId, AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
     ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
-    validateNotPendingManualReport(reportSeries);
-    reportSeries.setName(updateReportSeriesRequest.name());
-    reportSeries.setDescription(updateReportSeriesRequest.description());
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
-      reportSeries.getReports().getFirst().setName(updateReportSeriesRequest.name());
+
+    switch (updateReportSeriesRequest) {
+      case ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest ->
+          activateReportSeries(activateAutoReportSeriesRequest, reportSeries);
+      case DeactivateAutoReportSeriesRequest ignored -> deactivateReportSeries(reportSeries);
+      case UpdateNameAndDescriptionReportSeriesRequest
+                  updateNameAndDescriptionReportSeriesRequest ->
+          updateNameAndDescription(updateNameAndDescriptionReportSeriesRequest, reportSeries);
     }
 
     return ReportMapper.mapToApi(reportSeries);
   }
 
-  private void validateNotPendingManualReport(ReportSeries reportSeries) {
+  private void activateReportSeries(
+      ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest, ReportSeries reportSeries) {
+    validateIsAutoReportSeries(reportSeries);
+    reportSeries.setActive(true);
+    reportSeries.setStartMonth(activateAutoReportSeriesRequest.startMonth());
+    reportSeries.setFrequency(
+        ReportMapper.mapToFrequency(activateAutoReportSeriesRequest.frequency()));
+    reportSeries.setPeriod(
+        ReportMapper.mapToReportingPeriod(activateAutoReportSeriesRequest.reportingPeriod()));
+
+    Report plannedReport = getPlannedReport(reportSeries);
+    if (plannedReport == null) {
+      int nextNumber = reportService.findNextNumberInReports(reportSeries.getReports());
+      addNewPlannedReportToSeries(
+          reportSeries,
+          String.valueOf(nextNumber),
+          reportSeries.getStartMonth(),
+          reportSeries.getStatistic());
+    } else {
+      LocalDate executionAndEndDate = calculateExecutionDate(reportSeries.getStartMonth());
+      LocalDate dateStart =
+          ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
+
+      plannedReport.setTimeRangeStart(dateStart.atStartOfDay(clock.getZone()).toInstant());
+      plannedReport.setTimeRangeEnd(executionAndEndDate.atStartOfDay(clock.getZone()).toInstant());
+      plannedReport.setExecutionDate(executionAndEndDate);
+    }
+  }
+
+  private void deactivateReportSeries(ReportSeries reportSeries) {
+    validateIsAutoReportSeries(reportSeries);
+    if (reportSeries.isActive()) {
+      reportSeries.setActive(false);
+      Report plannedReport = getPlannedReport(reportSeries);
+      if (plannedReport != null) {
+        reportSeries.removeReport(plannedReport);
+      }
+    }
+  }
+
+  private static void validateIsAutoReportSeries(ReportSeries reportSeries) {
+    if (!reportSeries.getReportType().equals(ReportType.AUTO)) {
+      throw new BadRequestException(
+          "Report series %s is not of type 'AUTO'".formatted(reportSeries.getExternalId()));
+    }
+  }
+
+  private static Report getPlannedReport(ReportSeries reportSeries) {
+    return reportSeries.getReports().stream()
+        .filter(report -> report.getState().equals(AggregationResultState.PLANNED))
+        .findFirst()
+        .orElse(null);
+  }
+
+  private static void updateNameAndDescription(
+      UpdateNameAndDescriptionReportSeriesRequest updateNameAndDescriptionReportSeriesRequest,
+      ReportSeries reportSeries) {
+    validateNotPendingManualReport(reportSeries);
+
+    reportSeries.setName(updateNameAndDescriptionReportSeriesRequest.name());
+    reportSeries.setDescription(updateNameAndDescriptionReportSeriesRequest.description());
+    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      reportSeries
+          .getReports()
+          .getFirst()
+          .setName(updateNameAndDescriptionReportSeriesRequest.name());
+    }
+  }
+
+  private static void validateNotPendingManualReport(ReportSeries reportSeries) {
     if (reportSeries.getReportType().equals(ReportType.MANUAL)
         && reportSeries.getReports().getFirst().getState().equals(AggregationResultState.PENDING)) {
       throw new BadRequestException(
@@ -177,7 +261,7 @@ public class ReportSeriesService {
     reportSeriesRepository.delete(reportSeries);
   }
 
-  private void validateBelongsToCurrentUserOrIsAdmin(ReportSeries reportSeries) {
+  static void validateBelongsToCurrentUserOrIsAdmin(ReportSeries reportSeries) {
     UUID userId = CurrentUserHelper.getCurrentUserId();
     if (!userId.equals(reportSeries.getCreatedByUserId())
         && CurrentUserHelper.currentUserHasNoRole(
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
index 77149475021847c68a4d68bb8804417c92e818e6..a18a11549dd71a504e72d4677dfdcf015aa97a6b 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
@@ -37,6 +37,7 @@ import de.eshg.statistics.persistence.entity.report.ReportSeries;
 import de.eshg.statistics.persistence.entity.report.ReportType;
 import de.eshg.statistics.persistence.entity.report.ReportingPeriod;
 import de.eshg.statistics.persistence.repository.ReportRepository;
+import de.eshg.statistics.persistence.repository.ReportSeriesRepository;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -57,6 +58,7 @@ import org.springframework.transaction.annotation.Transactional;
 @Service
 public class ReportService {
   private final ReportRepository reportRepository;
+  private final ReportSeriesRepository reportSeriesRepository;
   private final StatisticService statisticService;
   private final Clock clock;
   private final DataAggregationService dataAggregationService;
@@ -66,11 +68,13 @@ public class ReportService {
 
   public ReportService(
       ReportRepository reportRepository,
+      ReportSeriesRepository reportSeriesRepository,
       StatisticService statisticService,
       Clock clock,
       DataAggregationService dataAggregationService,
       EvaluationService evaluationService) {
     this.reportRepository = reportRepository;
+    this.reportSeriesRepository = reportSeriesRepository;
     this.statisticService = statisticService;
     this.clock = clock;
     this.dataAggregationService = dataAggregationService;
@@ -107,8 +111,11 @@ public class ReportService {
   public GetReportDetailPageResponse getReportDetailPage(UUID reportId) {
     Report report = getReportInternal(reportId);
     validateReportCompleted(report);
-    Map<UUID, UserDto> resolvedUsers =
-        statisticService.getResolvedUsers(Set.of(report.getCreatedByUserId()));
+    UUID reportSeriesUserId = report.getReportSeries().getCreatedByUserId();
+    Set<UUID> userIds = new HashSet<>();
+    userIds.add(reportSeriesUserId);
+    userIds.add(report.getCreatedByUserId());
+    Map<UUID, UserDto> resolvedUsers = statisticService.getResolvedUsers(userIds);
     List<EvaluationDto> evaluations = EvaluationMapper.getEvaluations(report.getEvaluations());
 
     return new GetReportDetailPageResponse(
@@ -122,6 +129,7 @@ public class ReportService {
         report.getCreatedAt(),
         StatisticMapper.mapToApi(report.getTableColumns()),
         report.getNumberOfTableRows(),
+        resolvedUsers.get(reportSeriesUserId),
         resolvedUsers.get(report.getCreatedByUserId()),
         evaluations);
   }
@@ -161,6 +169,9 @@ public class ReportService {
   public void createNewPlannedReportInSeries(UUID reportId) {
     Report report = getReportInternal(reportId);
     ReportSeries reportSeries = report.getReportSeries();
+    if (!reportSeries.isActive()) {
+      return;
+    }
     int nextNumber = getNextNumber(report);
 
     LocalDate executionAndEndDate =
@@ -199,14 +210,31 @@ public class ReportService {
   }
 
   private int getNextNumber(Report report) {
+    return getNumberOfReport(report).orElse(report.getReportSeries().getReports().size()) + 1;
+  }
+
+  int findNextNumberInReports(List<Report> reports) {
+    int currentHighest = 0;
+    for (Report report : reports) {
+      Optional<Integer> numberOfReport = getNumberOfReport(report);
+      if (numberOfReport.isEmpty()) {
+        return reports.size() + 1;
+      } else if (numberOfReport.get() > currentHighest) {
+        currentHighest = numberOfReport.get();
+      }
+    }
+    return currentHighest + 1;
+  }
+
+  private Optional<Integer> getNumberOfReport(Report report) {
     try {
-      return Integer.parseInt(report.getName()) + 1;
+      return Optional.of(Integer.parseInt(report.getName()));
     } catch (NumberFormatException e) {
       log.error(
           "Report {} has name '{}' which is not a number",
           report.getExternalId(),
           report.getName());
-      return report.getReportSeries().getReports().size() + 1;
+      return Optional.empty();
     }
   }
 
@@ -488,4 +516,20 @@ public class ReportService {
       report.setState(AggregationResultState.FAILED);
     }
   }
+
+  @Transactional
+  public void deleteReport(UUID reportId) {
+    Report report = getReportInternal(reportId);
+    ReportSeries reportSeries = report.getReportSeries();
+    ReportSeriesService.validateBelongsToCurrentUserOrIsAdmin(reportSeries);
+    if (report.getState().equals(AggregationResultState.PLANNED)) {
+      throw new BadRequestException(
+          "Report is in state 'PLANNED', deactivate report series to remove this report");
+    }
+    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      reportSeriesRepository.delete(reportSeries);
+    } else {
+      reportRepository.delete(report);
+    }
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/GetDetailPageInformationResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/GetDetailPageInformationResponse.java
index e4f3f62407bb6fc997f6eab2fddd34c57fed10e8..ef7046cd01f824ccbca95d82a102ebe213ccc7d7 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/GetDetailPageInformationResponse.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/GetDetailPageInformationResponse.java
@@ -15,5 +15,5 @@ public record GetDetailPageInformationResponse(
     @NotNull @Valid StatisticInfo statisticInfo,
     @NotNull @Valid List<TableColumnHeader> tableColumnHeaders,
     @NotNull @Min(0) long totalNumberOfElements,
-    @Valid UserDto userDto,
+    @Valid UserDto user,
     @NotNull @Valid List<EvaluationDto> evaluations) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e912018ffcc1e1e55f84824008e2b2456caf0551
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(name = "AbstractUpdateReportSeriesRequest")
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    property = "@type",
+    include = JsonTypeInfo.As.EXISTING_PROPERTY)
+@JsonSubTypes({
+  @JsonSubTypes.Type(
+      value = ActivateAutoReportSeriesRequest.class,
+      name = ActivateAutoReportSeriesRequest.SCHEMA_NAME),
+  @JsonSubTypes.Type(
+      value = DeactivateAutoReportSeriesRequest.class,
+      name = DeactivateAutoReportSeriesRequest.SCHEMA_NAME),
+  @JsonSubTypes.Type(
+      value = UpdateNameAndDescriptionReportSeriesRequest.class,
+      name = UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME),
+})
+public sealed interface AbstractUpdateReportSeriesRequest
+    permits ActivateAutoReportSeriesRequest,
+        DeactivateAutoReportSeriesRequest,
+        UpdateNameAndDescriptionReportSeriesRequest {
+  @Hidden
+  @NotNull
+  @JsonProperty("@type")
+  String type();
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c48b81979df03fff9dffa338c9cfc3ebbb1f0a0
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import static de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest.SCHEMA_NAME;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(name = SCHEMA_NAME)
+public record ActivateAutoReportSeriesRequest(
+    @Min(1) @Max(12) @NotNull Integer startMonth,
+    @NotNull FrequencyDto frequency,
+    @NotNull ReportingPeriodDto reportingPeriod)
+    implements AbstractUpdateReportSeriesRequest {
+  public static final String SCHEMA_NAME = "ActivateAutoReportSeriesRequest";
+
+  @Override
+  public String type() {
+    return SCHEMA_NAME;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..57e0e7ec9d39b8d08e7e03ecff7ed4f2f138d41a
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import static de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest.SCHEMA_NAME;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = SCHEMA_NAME)
+public record DeactivateAutoReportSeriesRequest() implements AbstractUpdateReportSeriesRequest {
+  public static final String SCHEMA_NAME = "DeactivateAutoReportSeriesRequest";
+
+  @Override
+  public String type() {
+    return SCHEMA_NAME;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
index 4b873c19e520924d7cfb19d5d433c42d3ad08915..b7907c12ec1c3f2552f8d7dbd6defacf47d5c70b 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
@@ -27,5 +27,6 @@ public record GetReportDetailPageResponse(
     @NotNull Instant createdAt,
     @NotNull @Valid List<TableColumnHeader> tableColumnHeaders,
     @NotNull @Min(0) long totalNumberOfElements,
-    @Valid UserDto userDto,
+    @Valid UserDto userReportSeries,
+    @Valid UserDto userReport,
     @NotNull @Valid List<EvaluationDto> evaluation) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..673396837d75c1a3207d886d1ad7fbafd06ba65f
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import static de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = SCHEMA_NAME)
+public record UpdateNameAndDescriptionReportSeriesRequest(@NotBlank String name, String description)
+    implements AbstractUpdateReportSeriesRequest {
+  public static final String SCHEMA_NAME = "UpdateNameAndDescriptionReportSeriesRequest";
+
+  @Override
+  public String type() {
+    return SCHEMA_NAME;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
deleted file mode 100644
index f40b7ee98e266e2ab55d3f3abc6bacd345f77741..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import jakarta.validation.constraints.NotBlank;
-
-public record UpdateReportSeriesRequest(@NotBlank String name, String description) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/CellEntry.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/CellEntry.java
index 277c271a7f37700d8346531396b3bfce1d71a167..9a7f74c1d97b860f38b9cc7a82decabc52d9eec6 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/CellEntry.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/CellEntry.java
@@ -7,7 +7,7 @@ package de.eshg.statistics.persistence.entity;
 
 import static de.eshg.lib.common.SensitivityLevel.PUBLIC;
 
-import de.eshg.domain.model.BaseEntity;
+import de.eshg.domain.model.SequencedBaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import jakarta.persistence.DiscriminatorColumn;
 import jakarta.persistence.Entity;
@@ -26,7 +26,7 @@ import jakarta.persistence.UniqueConstraint;
 @Table(
     uniqueConstraints = @UniqueConstraint(columnNames = {"table_column_id", "table_row_id"}),
     indexes = @Index(columnList = "table_row_id"))
-public abstract class CellEntry extends BaseEntity {
+public abstract class CellEntry extends SequencedBaseEntity {
 
   @DataSensitivity(PUBLIC)
   @ManyToOne(fetch = FetchType.LAZY, optional = false)
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableRow.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableRow.java
index d7323d9c16ae2ee2e1d8f8d85b9322a790c5b473..14154404062294cfc6bddc0fe86ff7d4f3876938 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableRow.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableRow.java
@@ -7,7 +7,7 @@ package de.eshg.statistics.persistence.entity;
 
 import static de.eshg.lib.common.SensitivityLevel.PUBLIC;
 
-import de.eshg.domain.model.BaseEntity;
+import de.eshg.domain.model.SequencedBaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Entity;
@@ -24,7 +24,7 @@ import java.util.List;
 @Entity
 @DataSensitivity(PUBLIC)
 @Table(indexes = @Index(columnList = "aggregation_result_id"))
-public class TableRow extends BaseEntity {
+public class TableRow extends SequencedBaseEntity {
   @ManyToOne(fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name = "aggregation_result_id")
   private AbstractAggregationResult aggregationResult;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/ReportSeries.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/ReportSeries.java
index 482e389fd5dad570c67423c5e566e8505d66d91e..4aaf9c63f830fef77745648763928bb8643f6fa0 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/ReportSeries.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/ReportSeries.java
@@ -193,6 +193,11 @@ public class ReportSeries extends BaseEntityWithExternalId {
     reports.add(report);
   }
 
+  public void removeReport(Report report) {
+    report.setReportSeries(null);
+    this.reports.remove(report);
+  }
+
   public List<Report> getReports() {
     return reports;
   }
diff --git a/backend/statistics/src/main/resources/application.properties b/backend/statistics/src/main/resources/application.properties
index 086c9691cbd7e0c2026a3507208841b57f6cd31c..c0e60cd36730c4746c9b873084671d739fc6d4db 100644
--- a/backend/statistics/src/main/resources/application.properties
+++ b/backend/statistics/src/main/resources/application.properties
@@ -3,6 +3,8 @@ spring.datasource.url=jdbc:postgresql://localhost:5440/statistics
 spring.datasource.username=testuser
 spring.datasource.password=testpassword
 spring.jpa.hibernate.ddl-auto=validate
+spring.jpa.properties.hibernate.jdbc.batch_size=250
+spring.jpa.properties.hibernate.order_inserts=true
 
 # Enable explicitly since we disable it by default via common-persistence.properties
 spring.liquibase.enabled=true
diff --git a/backend/statistics/src/main/resources/migrations/0023_tablerow_cellentry_sequences.xml b/backend/statistics/src/main/resources/migrations/0023_tablerow_cellentry_sequences.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c023d17346a618cea08774483a6a901f659bbbb4
--- /dev/null
+++ b/backend/statistics/src/main/resources/migrations/0023_tablerow_cellentry_sequences.xml
@@ -0,0 +1,15 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="migrate_tablerow_cellentry_to_sequence_based_ids">
+    <ext:migrateAutoIncrementToSequence tableName="table_row"/>
+    <ext:migrateAutoIncrementToSequence tableName="cell_entry"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/statistics/src/main/resources/migrations/changelog.xml b/backend/statistics/src/main/resources/migrations/changelog.xml
index f81dd3b83e2b5ba5c2d7769c906303edc3980e8d..46616a1dc0704259451c3bb126e64200069bced2 100644
--- a/backend/statistics/src/main/resources/migrations/changelog.xml
+++ b/backend/statistics/src/main/resources/migrations/changelog.xml
@@ -30,5 +30,6 @@
     <include file="migrations/0020_introduce_reports.xml"/>
     <include file="migrations/0021_cell_entry_indexes.xml"/>
     <include file="migrations/0022_auto_report_series.xml"/>
+    <include file="migrations/0023_tablerow_cellentry_sequences.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/test-commons/build.gradle b/backend/test-commons/build.gradle
index 3ea67cf1b4dd097afc2a9802c3ff9e245025bccb..82eb76c1a21807a5c35d7095d87b4254d2b1d2c9 100644
--- a/backend/test-commons/build.gradle
+++ b/backend/test-commons/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     api project(':util-commons')
     api project(':lib-keycloak')
     api project(':test-helper-commons-api')
+    implementation project(':test-helper-commons-spring')
 
     implementation 'de.cronn:postgres-snapshot-util:latest.release'
     implementation 'org.springframework:spring-webmvc'
diff --git a/backend/test-helper-commons-spring/src/main/java/de/eshg/testhelper/ConditionalOnLocalEnvironment.java b/backend/test-helper-commons-spring/src/main/java/de/eshg/testhelper/ConditionalOnLocalEnvironment.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a4a407215ad5f32c0fc2e65f4c80f79df3565f1
--- /dev/null
+++ b/backend/test-helper-commons-spring/src/main/java/de/eshg/testhelper/ConditionalOnLocalEnvironment.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.testhelper;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.springframework.context.annotation.Profile;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Profile(ConditionalOnLocalEnvironment.LOCAL_PROFILE_NAME)
+public @interface ConditionalOnLocalEnvironment {
+  String LOCAL_PROFILE_NAME = "local";
+}
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/LocalProfileAutoConfiguration.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/LocalProfileAutoConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..e97809579bd3702b18cdf14b3c07f3f409a60c67
--- /dev/null
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/LocalProfileAutoConfiguration.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.testhelper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.PropertySource;
+
+@AutoConfiguration
+@ConditionalOnLocalEnvironment
+@PropertySource("classpath:/common-local.properties")
+public class LocalProfileAutoConfiguration {
+
+  private static final Logger log = LoggerFactory.getLogger(LocalProfileAutoConfiguration.class);
+
+  LocalProfileAutoConfiguration() {
+    log.info("{} is active", this);
+  }
+}
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/TestHelperAutoConfiguration.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/TestHelperAutoConfiguration.java
index a91548b87cd0012097f47817487c9976b93a47dd..0a86f0866dc159e7405ee6d9b47d5def1ff63afc 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/TestHelperAutoConfiguration.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/TestHelperAutoConfiguration.java
@@ -10,7 +10,6 @@ import de.eshg.testhelper.population.PopulateWithAccessTokenHelper;
 import java.time.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration;
@@ -38,19 +37,15 @@ public class TestHelperAutoConfiguration {
   }
 
   @Bean
-  @ConditionalOnProperty(
-      value = "eshg.testclock.enabled",
-      havingValue = "true",
-      matchIfMissing = true)
-  public TestHelperClock testHelperClock() {
+  @ConditionalOnProperty(value = "eshg.testclock.enabled", havingValue = "true")
+  TestHelperClock testHelperClock() {
     TestHelperClock testHelperClock = TestHelperClock.defaultBerlin();
     log.warn("Using {}", testHelperClock.getClass().getSimpleName());
     return testHelperClock;
   }
 
   @Bean
-  public ValidationConfigurationCustomizer clockProviderConfigurationCustomizer(
-      @Autowired Clock clock) {
+  ValidationConfigurationCustomizer clockProviderConfigurationCustomizer(Clock clock) {
     return configuration -> configuration.clockProvider(() -> clock);
   }
 }
diff --git a/backend/test-helper-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/backend/test-helper-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index af7c1e09cf87fed1914119120735e2f203d9a291..f612d88f395c0c56662fe642907fc46fd50a85f0 100644
--- a/backend/test-helper-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/backend/test-helper-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1,2 @@
 de.eshg.testhelper.TestHelperAutoConfiguration
+de.eshg.testhelper.LocalProfileAutoConfiguration
diff --git a/backend/test-helper-commons/src/main/resources/common-local.properties b/backend/test-helper-commons/src/main/resources/common-local.properties
new file mode 100644
index 0000000000000000000000000000000000000000..369d1e119a76d520c5ca4f585dc0d78f7f325c0f
--- /dev/null
+++ b/backend/test-helper-commons/src/main/resources/common-local.properties
@@ -0,0 +1 @@
+eshg.testclock.enabled=true
diff --git a/backend/travel-medicine/build.gradle b/backend/travel-medicine/build.gradle
index 6c66d08e18fa63da109aec76355d3aacc350475a..fae4daa5d6031843a25614cf13571a3646becf66 100644
--- a/backend/travel-medicine/build.gradle
+++ b/backend/travel-medicine/build.gradle
@@ -16,6 +16,7 @@ dependencies {
     runtimeOnly 'org.postgresql:postgresql'
     testImplementation testFixtures(project(':business-module-persistence-commons'))
     testImplementation testFixtures(project(':lib-document-generator'))
+    testImplementation testFixtures(project(':base-api'))
 }
 
 dockerCompose {
@@ -33,4 +34,4 @@ tasks.named("test").configure {
 
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-travel-medicine') ?: "unspecified"
-}
\ No newline at end of file
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
index 40876be6925c73bd0745b945b584bd2b62e9e1a8..7139420a7d74d92e6198e0f8706f77b52a4e83a1 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
@@ -9,6 +9,7 @@ import de.eshg.lib.common.BusinessModule;
 import de.eshg.rest.service.security.config.TravelMedicinePublicSecurityConfig;
 import de.eshg.travelmedicine.citizenpublic.DepartmentInfoProperties;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
+import de.eshg.travelmedicine.notification.NotificationProperties;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -17,7 +18,11 @@ import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
 @Import(TravelMedicinePublicSecurityConfig.class)
-@EnableConfigurationProperties({TravelMedicineFeatureToggle.class, DepartmentInfoProperties.class})
+@EnableConfigurationProperties({
+  TravelMedicineFeatureToggle.class,
+  DepartmentInfoProperties.class,
+  NotificationProperties.class
+})
 public class TravelMedicineApplication {
 
   @Bean
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/MailClient.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/MailClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..e94848e8252a31b35e27165949ba72df90582a0c
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/MailClient.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.notification;
+
+import de.eshg.base.mail.MailApi;
+import de.eshg.base.mail.SendEmailRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MailClient {
+  private static final Logger log = LoggerFactory.getLogger(MailClient.class);
+
+  private final MailApi mailApi;
+
+  public MailClient(MailApi mailApi) {
+    this.mailApi = mailApi;
+  }
+
+  void sendMail(String to, String from, String subject, String text) {
+    log.info("Sending E-Mail notification");
+
+    SendEmailRequest sendEmailRequest = new SendEmailRequest(to, from, subject, text);
+    mailApi.sendEmail(sendEmailRequest);
+
+    log.info("E-Mail notification send");
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationProperties.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..b24922b5e0122c7eb5c330123f9e201fba27e39f
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationProperties.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.notification;
+
+import jakarta.validation.constraints.NotNull;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties(prefix = "de.eshg.travel-medicine.notification")
+public record NotificationProperties(@NotNull String fromAddress, @NotNull String greeting) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0d52a9cabf869e001b9f9d9c5a626e6d1f2e705
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.notification;
+
+import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.util.UriComponentsBuilder;
+
+@Service
+public class NotificationService {
+
+  private static final String CITIZEN_PORTAL_RMBI_LOGIN_PATH = "impfberatung/meine-termine";
+
+  private final MailClient mailClient;
+  private final ModuleClientAuthenticator moduleClientAuthenticator;
+  private final NotificationProperties notificationProperties;
+  private final String citizenPortalUrl;
+
+  public NotificationService(
+      MailClient mailClient,
+      ModuleClientAuthenticator moduleClientAuthenticator,
+      NotificationProperties notificationProperties,
+      @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl) {
+    this.mailClient = mailClient;
+    this.moduleClientAuthenticator = moduleClientAuthenticator;
+    this.notificationProperties = notificationProperties;
+    this.citizenPortalUrl = citizenPortalUrl;
+    ;
+  }
+
+  public void onNewCitizenProcedure(
+      String accessCode, PatientDto patientDto, ProcedureStep procedureStep) {
+    String text =
+        NotificationText.getNewCitizenProcedureBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            procedureStep.getAppointment().getAppointmentStart(),
+            buildLoginUrl(accessCode),
+            accessCode,
+            notificationProperties.greeting());
+
+    moduleClientAuthenticator.doWithModuleClientAuthentication(
+        () ->
+            mailClient.sendMail(
+                patientDto.emailAddresses().getFirst(),
+                notificationProperties.fromAddress(),
+                NotificationText.getNewCitizenProcedureSubject(),
+                text));
+  }
+
+  private String buildLoginUrl(String accessCode) {
+    return UriComponentsBuilder.fromUriString(citizenPortalUrl)
+        .pathSegment(CITIZEN_PORTAL_RMBI_LOGIN_PATH)
+        .queryParam("access_code", accessCode)
+        .build()
+        .toUriString();
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
new file mode 100644
index 0000000000000000000000000000000000000000..291b97bb00d4a95b839672fba1ee2724735a7a0d
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.notification;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+public class NotificationText {
+  private static final DateTimeFormatter APPOINTMENT_START_FORMAT =
+      DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm", Locale.GERMAN);
+
+  private static final String NEW_CITIZEN_PROCEDURE_SUBJECT = "Deine Terminbuchung bei uns!";
+  private static final String NEW_CITIZEN_PROCEDURE_BODY =
+      """
+      Sehr geehrte(r) %s %s,
+
+    wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr erfolgreich eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
+    Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
+    %s
+
+    Anmeldecode: %s
+    Zur Verifikation wird Ihr Geburtsdatum benötigt.
+
+    Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Stornierungen verwenden Sie bitte den angegebenen Link.
+
+    Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf Sie bald bei uns begrüßen zu dürfen.
+
+      Mit freundlichen Grüßen,
+
+      %s""";
+
+  public static String getNewCitizenProcedureSubject() {
+    return NEW_CITIZEN_PROCEDURE_SUBJECT;
+  }
+
+  public static String getNewCitizenProcedureBody(
+      String firstName,
+      String lastName,
+      Instant appointmentStart,
+      String loginUrl,
+      String accessCode,
+      String greeting) {
+    return String.format(
+        NEW_CITIZEN_PROCEDURE_BODY,
+        firstName,
+        lastName,
+        APPOINTMENT_START_FORMAT.format(appointmentStart.atZone(ZoneId.of("Europe/Berlin"))),
+        loginUrl,
+        accessCode,
+        greeting);
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
index caf8f86483f0f9384dc219f1504c91d54dbbe64b..dd3e566d11f45e2140c95287d37b2154647fcad3 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
@@ -21,6 +21,7 @@ import de.eshg.travelmedicine.informationstatementtemplate.persistence.entity.In
 import de.eshg.travelmedicine.informationstatementtemplate.persistence.entity.InformationStatementTemplateRepository;
 import de.eshg.travelmedicine.informationstatementtemplate.persistence.entity.InformationStatementTemplateState;
 import de.eshg.travelmedicine.medicalhistory.MedicalHistoryService;
+import de.eshg.travelmedicine.notification.NotificationService;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppliedServiceDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingTypeDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentOverviewEntryDto;
@@ -106,6 +107,7 @@ public class VaccinationConsultationService {
       "The list of travel destinations must not contain null elements.";
   private final ProcedureAccessor procedureAccessor;
   private final InformationStatementTemplateRepository informationStatementTemplateRepository;
+  private final NotificationService notificationService;
 
   public VaccinationConsultationService(
       VaccinationConsultationRepository vaccinationConsultationRepository,
@@ -124,7 +126,8 @@ public class VaccinationConsultationService {
       Clock clock,
       AuditLogger auditLogger,
       ProcedureAccessor procedureAccessor,
-      InformationStatementTemplateRepository informationStatementTemplateRepository) {
+      InformationStatementTemplateRepository informationStatementTemplateRepository,
+      NotificationService notificationService) {
     this.vaccinationConsultationRepository = vaccinationConsultationRepository;
     this.procedureStepRepository = procedureStepRepository;
     this.procedureStepService = procedureStepService;
@@ -142,6 +145,7 @@ public class VaccinationConsultationService {
     this.auditLogger = auditLogger;
     this.procedureAccessor = procedureAccessor;
     this.informationStatementTemplateRepository = informationStatementTemplateRepository;
+    this.notificationService = notificationService;
   }
 
   public UUID createProcedure(PostVaccinationConsultationRequest request) {
@@ -196,6 +200,9 @@ public class VaccinationConsultationService {
     vaccinationConsultationRepository.save(vaccinationConsultation);
     procedureStepRepository.save(initialProcedureStep);
 
+    notificationService.onNewCitizenProcedure(
+        citizenAccessCodeUser.accessCode(), request.patient(), initialProcedureStep);
+
     return vaccinationConsultation.getExternalId();
   }
 
diff --git a/backend/travel-medicine/src/main/resources/application.properties b/backend/travel-medicine/src/main/resources/application.properties
index 5e761c9868896cf9beffc8a95a1530e658f2e49b..c50541ac746fe781d8929723d83e568bd7eb525c 100644
--- a/backend/travel-medicine/src/main/resources/application.properties
+++ b/backend/travel-medicine/src/main/resources/application.properties
@@ -21,6 +21,11 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[VACCINATION]=15
 
 de.eshg.lib.appointmentblock.createAppointmentBlockForCurrentUser=false
 
+eshg.citizen-portal.reverse-proxy.url=http://localhost:4001
+
+de.eshg.travel-medicine.notification.fromAddress=info.reisemedizin@stadt-frankfurt.de
+de.eshg.travel-medicine.notification.greeting=Ihr Impfberatungsteam der Stadt Frankfurt
+
 # can be set individually to overwrite base department infos
 # de.eshg.travel-medicine.department-info.name=
 # de.eshg.travel-medicine.department-info.abbreviation=
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
index 7d157b55f9e6f5bdaf7a55ec70880010f79b24b4..a3bc9dbbfe87a33b32989f0a25eb7ad83c478d16 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
@@ -41,6 +41,9 @@
     "preliminaryCourse": "Vorlaufkurs",
     "schoolName": "Name der zuständigen Schule",
     "interests": "Interessen und besondere Fähigkeiten",
+    "clubSportAndOther": "Was macht Ihr Kind besonders gerne?",
+    "clubSport": "Sport im Verein",
+    "otherInterests": "Sonstiges",
     "canSwim": "Kann Ihr Kind schwimmen?",
     "seahorseBadge": "Hat Ihr Kind das Seepferdchenabzeichen?",
     "personalCharacteristics": "Persönliche Besonderheiten",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
index 622c57a115bda6bbd1a1710fcb7722c00ba09261..735127c7acf4c0d3aa82ac60a822ec5c5452180e 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
@@ -41,6 +41,9 @@
     "preliminaryCourse": "Preliminary Course",
     "schoolName": "Name of the Responsible School",
     "interests": "Interests and Special Skills",
+    "clubSportAndOther": "What does your child particularly enjoy doing?",
+    "clubSport": "Club Sport",
+    "otherInterests": "Other",
     "canSwim": "Can your child swim?",
     "seahorseBadge": "Does your child have the Seahorse Badge?",
     "personalCharacteristics": "Personal Characteristics",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
index 10e46d90ce80cf4a17f611e9502e7f1a2978854e..fdb8f2cf69d4cd35176086270352c8ee326a1aeb 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
@@ -75,9 +75,9 @@ interface MigrationBackgroundValues {
 interface PromotionBeforeSchoolEntryValues {
   earlySupport: boolean | null;
   integrationPlace: boolean | null;
-  ergotherapy: OptionalFieldValue<boolean>;
-  speechTherapy: OptionalFieldValue<boolean>;
-  physiotherapy: OptionalFieldValue<boolean>;
+  ergotherapy: boolean | null;
+  speechTherapy: boolean | null;
+  physiotherapy: boolean | null;
 }
 
 interface AdditionalChildInfoValues {
@@ -96,6 +96,8 @@ interface DaycareAndSchoolInfoValues {
 interface InterestAndSportsInfoValues {
   canSwim: boolean | null;
   hasSeahorseBadge: boolean | null;
+  clubSport: OptionalFieldValue<string>;
+  otherInterests: OptionalFieldValue<string>;
 }
 
 interface DevelopmentInfoValues {
@@ -133,15 +135,15 @@ interface PromotionTherapyAndAidInfoValues {
   hearingAid: ToggleableSectionFormValue & {
     which: OptionalFieldValue<string>;
   };
-  speechTherapy: ToggleableSectionFormValue & {
+  speechTherapy: {
     start: OptionalFieldValue<string>;
     end: OptionalFieldValue<string>;
   };
-  ergoTherapy: ToggleableSectionFormValue & {
+  ergoTherapy: {
     start: OptionalFieldValue<string>;
     end: OptionalFieldValue<string>;
   };
-  physioTherapy: ToggleableSectionFormValue & {
+  physioTherapy: {
     start: OptionalFieldValue<string>;
     end: OptionalFieldValue<string>;
   };
@@ -151,7 +153,7 @@ interface PromotionTherapyAndAidInfoValues {
 }
 
 interface ToggleableSectionFormValue {
-  show: boolean;
+  show: boolean | null;
 }
 
 const INITIAL_VALUES: CitizenAnamnesisFormValues = {
@@ -168,7 +170,7 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
       nationality: "",
     },
     secondParent: {
-      show: false,
+      show: null,
       countryOfBirth: "",
       nationality: "",
     },
@@ -176,14 +178,14 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
   promotionBeforeSchoolEntry: {
     earlySupport: null,
     integrationPlace: null,
-    ergotherapy: "",
-    speechTherapy: "",
-    physiotherapy: "",
+    ergotherapy: null,
+    speechTherapy: null,
+    physiotherapy: null,
   },
   additionalChildInfo: {
     responsiblePhysician: "",
     siblings: {
-      show: false,
+      show: null,
       birthYears: [""],
     },
   },
@@ -195,6 +197,8 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
   interestsAndSportsInfo: {
     canSwim: null,
     hasSeahorseBadge: null,
+    clubSport: "",
+    otherInterests: "",
   },
   personalConspicuities: null,
   developmentInfo: {
@@ -205,7 +209,7 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
   },
   illnessAndAccidentInfo: {
     allergies: {
-      show: false,
+      show: null,
       values: [""],
     },
     severeIllnesses: null,
@@ -216,7 +220,7 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
   familyHistoryInfo: {
     spectaclesInFamily: null,
     chronicIllnessOrDisabilityInFamily: {
-      show: false,
+      show: null,
       value: "",
     },
   },
@@ -226,34 +230,31 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
     speechImpairment: null,
     spectacles: {
       since: "",
-      show: false,
+      show: null,
     },
     visionSchool: {
       since: "",
-      show: false,
+      show: null,
     },
     hearingAid: {
       which: "",
-      show: false,
+      show: null,
     },
     speechTherapy: {
       start: "",
       end: "",
-      show: false,
     },
     ergoTherapy: {
       start: "",
       end: "",
-      show: false,
     },
     physioTherapy: {
       start: "",
       end: "",
-      show: false,
     },
     additionalTherapies: {
       which: "",
-      show: false,
+      show: null,
     },
   },
 };
@@ -284,6 +285,7 @@ export function CitizenAnamnesisForm(props: CitizenAnamnesisFormProps) {
       })
       .catch();
   }
+
   return (
     <MultiStepForm<CitizenAnamnesisFormValues> steps={STEPS}>
       {({ Outlet, currentStep, totalSteps }) => (
@@ -359,31 +361,33 @@ function mapToRequest(
         nationalityFirstParent: mapOptionalValue(
           values.migrationBackground.firstParent.nationality,
         ),
-        countryOfBirthSecondParent: onlyIfShown(
+        countryOfBirthSecondParent: fallbackIfExplicitlyHidden(
           values.migrationBackground.secondParent,
           mapOptionalValue(
             values.migrationBackground.secondParent.countryOfBirth,
           ),
+          ApiSchoolEntryCountryCode.Uuu,
         ),
-        nationalitySecondParent: onlyIfShown(
+        nationalitySecondParent: fallbackIfExplicitlyHidden(
           values.migrationBackground.secondParent,
           mapOptionalValue(values.migrationBackground.secondParent.nationality),
+          ApiSchoolEntryCountryCode.Uuu,
         ),
       },
       promotionBeforeSchoolEntry: {
         earlySupport: mapNullableValue(
           values.promotionBeforeSchoolEntry.earlySupport,
         ),
-        ergotherapy: mapOptionalValue(
+        ergotherapy: mapNullableValue(
           values.promotionBeforeSchoolEntry.ergotherapy,
         ),
         integrationPlace: mapNullableValue(
           values.promotionBeforeSchoolEntry.integrationPlace,
         ),
-        physiotherapy: mapOptionalValue(
+        physiotherapy: mapNullableValue(
           values.promotionBeforeSchoolEntry.physiotherapy,
         ),
-        speechTherapy: mapOptionalValue(
+        speechTherapy: mapNullableValue(
           values.promotionBeforeSchoolEntry.speechTherapy,
         ),
       },
@@ -394,8 +398,8 @@ function mapToRequest(
         siblingsBirthYears: values.additionalChildInfo.siblings.show
           ? dropBlankStrings(
               values.additionalChildInfo.siblings.birthYears,
-            ).map(parseInt)
-          : [],
+            ).map((it) => parseInt(it))
+          : undefined,
       },
       daycareAndSchoolInfo: {
         inDaycareSince: mapMonthAndYear(
@@ -447,6 +451,10 @@ function mapToRequest(
         hasSeahorseBadge: mapNullableValue(
           values.interestsAndSportsInfo.hasSeahorseBadge,
         ),
+        clubSport: mapOptionalValue(values.interestsAndSportsInfo.clubSport),
+        otherInterests: mapOptionalValue(
+          values.interestsAndSportsInfo.otherInterests,
+        ),
       },
       promotionTherapyAndAidInfo: {
         visionImpairment: mapNullableValue(
@@ -471,31 +479,31 @@ function mapToRequest(
           mapOptionalValue(values.promotionTherapyAndAidInfo.hearingAid.which),
         ),
         speechTherapyStart: onlyIfShown(
-          values.promotionTherapyAndAidInfo.speechTherapy,
+          { show: values.promotionBeforeSchoolEntry.speechTherapy },
           mapOptionalDate(
             values.promotionTherapyAndAidInfo.speechTherapy.start,
           ),
         ),
         speechTherapyEnd: onlyIfShown(
-          values.promotionTherapyAndAidInfo.speechTherapy,
+          { show: values.promotionBeforeSchoolEntry.speechTherapy },
           mapOptionalDate(values.promotionTherapyAndAidInfo.speechTherapy.end),
         ),
         ergoTherapyStart: onlyIfShown(
-          values.promotionTherapyAndAidInfo.ergoTherapy,
+          { show: values.promotionBeforeSchoolEntry.ergotherapy },
           mapOptionalDate(values.promotionTherapyAndAidInfo.ergoTherapy.start),
         ),
         ergoTherapyEnd: onlyIfShown(
-          values.promotionTherapyAndAidInfo.ergoTherapy,
+          { show: values.promotionBeforeSchoolEntry.ergotherapy },
           mapOptionalDate(values.promotionTherapyAndAidInfo.ergoTherapy.end),
         ),
         physioTherapyStart: onlyIfShown(
-          values.promotionTherapyAndAidInfo.physioTherapy,
+          { show: values.promotionBeforeSchoolEntry.physiotherapy },
           mapOptionalDate(
             values.promotionTherapyAndAidInfo.physioTherapy.start,
           ),
         ),
         physioTherapyEnd: onlyIfShown(
-          values.promotionTherapyAndAidInfo.physioTherapy,
+          { show: values.promotionBeforeSchoolEntry.physiotherapy },
           mapOptionalDate(values.promotionTherapyAndAidInfo.physioTherapy.end),
         ),
         additionalTherapies: onlyIfShown(
@@ -516,3 +524,10 @@ function onlyIfShown<TValue>(
 ): TValue | undefined {
   return section.show ? value : undefined;
 }
+function fallbackIfExplicitlyHidden<TValue>(
+  section: ToggleableSectionFormValue,
+  value: TValue,
+  fallback: TValue,
+): TValue {
+  return section.show === false ? fallback : value;
+}
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
index aec7462c44af73206425ed8f75102773f4a9032b..2e35ad9fff80fc0851bbaba3006786359af01a99 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
@@ -20,6 +20,10 @@ export function CitizenAnamnesisStepFour() {
     "promotionTherapyAndAidInfo",
   );
 
+  const promotionBeforeSchoolEntry = createFieldNameMapper(
+    "promotionBeforeSchoolEntry",
+  );
+
   return (
     <ContentSheet>
       <Typography level="h3">{t("support.title")}</Typography>
@@ -102,7 +106,7 @@ export function CitizenAnamnesisStepFour() {
       </ToggleableSection>
       <Typography level="h4">{t("support.therapy.title")}</Typography>
       <ToggleableSection
-        name={promotionTherapyAndAidInfo("speechTherapy.show")}
+        name={promotionBeforeSchoolEntry("speechTherapy")}
         title={t("support.therapy.speechTherapy")}
       >
         <Typography level="body-sm">{t("support.therapy.date")}</Typography>
@@ -124,7 +128,7 @@ export function CitizenAnamnesisStepFour() {
         </Grid>
       </ToggleableSection>
       <ToggleableSection
-        name={promotionTherapyAndAidInfo("ergoTherapy.show")}
+        name={promotionBeforeSchoolEntry("ergotherapy")}
         title={t("support.therapy.ergoTherapy")}
       >
         <Typography level="body-sm">{t("support.therapy.date")}</Typography>
@@ -146,7 +150,7 @@ export function CitizenAnamnesisStepFour() {
         </Grid>
       </ToggleableSection>
       <ToggleableSection
-        name={promotionTherapyAndAidInfo("physioTherapy.show")}
+        name={promotionBeforeSchoolEntry("physiotherapy")}
         title={t("support.therapy.physioTherapy")}
       >
         <Typography level="body-sm">{t("support.therapy.date")}</Typography>
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepOne.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepOne.tsx
index f80d5be0fa9251e3f9f04e081241eef66b252e77..a68ba0d9d3bc24de0df0c7f64ab8ebcd2cc70127 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepOne.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepOne.tsx
@@ -97,7 +97,7 @@ function ContactForm(props: ContactFormProps) {
             {t("migration.inGermanySince")}
           </FormLabel>
           <LocalMonthAndYearFields
-            fieldName={migrationBackground("migration.inGermanySince")}
+            fieldName={migrationBackground("child.inGermanySince")}
             date={props.values.migrationBackground.child.inGermanySince}
           />
         </Grid>
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
index 7a4aaee1225fbb3d35d6bb5ef131419357e17a70..e911d0f435399ca691b46eae8478e6c4b91074f6 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
@@ -168,6 +168,23 @@ export function CitizenAnamnesisStepTwo({
       <Typography level="h4" component="h3">
         {t("additionalInfo.interests")}
       </Typography>
+      <Typography level="title-md">
+        {t("additionalInfo.clubSportAndOther")}
+      </Typography>
+      <Grid container sx={{ flexGrow: 1 }} spacing={2}>
+        <Grid xxs={12} lg={6}>
+          <InputField
+            name={interestsAndSportsInfo("clubSport")}
+            label={t("additionalInfo.clubSport")}
+          />
+        </Grid>
+        <Grid xxs={12} lg={6}>
+          <InputField
+            name={interestsAndSportsInfo("otherInterests")}
+            label={t("additionalInfo.otherInterests")}
+          />
+        </Grid>
+      </Grid>
       <LocalBooleanRadioField
         name={interestsAndSportsInfo("canSwim")}
         label={
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/components/ToggleableSection.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/components/ToggleableSection.tsx
index e059f85b2dcbff0077736dc3aeebd942ecd1ea93..01639f51567c745f19f11de8b91d1a4a0067d8cf 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/components/ToggleableSection.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/components/ToggleableSection.tsx
@@ -33,6 +33,7 @@ export function ToggleableSection({
         }
         trueLabel={t("yes")}
         falseLabel={t("no")}
+        allowDeselection
       />
       {field.value && children}
     </Stack>
diff --git a/docs/flaky-tests.adoc b/docs/flaky-tests.adoc
new file mode 100644
index 0000000000000000000000000000000000000000..90ccd7875a8f120a821d153e8a2e052b0ce9005e
--- /dev/null
+++ b/docs/flaky-tests.adoc
@@ -0,0 +1,44 @@
+= Flaky Tests
+:sectnums:
+
+Due to their nature, flaky tests can be hard to reproduce locally. Sometimes, the reason for a flakiness is not obvious. Then it's essential to reproduce the test failure locally in order to identify the cause and come up with a stable fix.
+
+== Reproducing flaky tests
+
+=== Stressing the CPU
+
+When flakiness comes from a high workload on the machine running the test (e.g. a Gitlab Runner), the `stress` command is a reliable utility to reproduce flakiness. It can be used to put high load on your CPUs before executing the test to be analyzed.
+
+[source, shell]
+----
+stress -c 16
+----
+
+`-c` denotes the number of CPUs to put under stress. This value is depending on your system. In most cases, the number of CPUs available on your system is a good choice.
+
+`stress` can also be used to stress your local memory and hard drive. However, stressing the CPU is usually sufficient to reproduce flakiness.
+
+Note: `stress` is only available for UNIX systems. Windows users will need another tool.
+
+=== Retry until failure
+
+Flakiness can also be reproduced by retrying a test continuously. While this can be done manually, it's time-consuming and cumbersome. The `retry` command helps to automate this process. It can be configured to retry a command until it fails:
+
+[source, shell]
+----
+retry --until=fail [--times=50] -- command-to-retry
+----
+
+Configuring `--times` is optional, but is useful to make the command terminate in case it hits no failure.
+Consider setting `--delay=0` to disable the back-off time after each attempt.
+After `--` follows the command to execute the test to analyze.
+
+.Playwright Example
+[source, shell]
+----
+retry --until=fail -- pnpm playwright test path/to/test.spec.ts:123
+----
+
+`retry` is also a useful utility to verify whether a potential fix actually stabilizes a flaky test.
+
+Note: `retry` is only available for UNIX systems. Windows users will need another tool.
diff --git a/employee-portal/src/app/(baseModule)/metrics/[businessModuleName]/[procedureType]/page.tsx b/employee-portal/src/app/(baseModule)/metrics/[businessModuleName]/[procedureType]/page.tsx
index dc99827b43aa0c676dcfb2b218747f7cf9ef9be9..dd944828a6dfc170e2261413b6d3ea9d8270386d 100644
--- a/employee-portal/src/app/(baseModule)/metrics/[businessModuleName]/[procedureType]/page.tsx
+++ b/employee-portal/src/app/(baseModule)/metrics/[businessModuleName]/[procedureType]/page.tsx
@@ -6,48 +6,33 @@
 "use client";
 
 import { ApiProcedureType } from "@eshg/employee-portal-api/base";
-import { endOfToday } from "date-fns";
-import { useState } from "react";
 
-import { useTaskMetricsQuery } from "@/lib/baseModule/api/queries/taskMetrics";
-import { lastXMonthsInDate } from "@/lib/baseModule/components/procedureMetrics/rangeSelectHelper";
+import { TaskMetricsDisplay } from "@/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay";
 import { routes } from "@/lib/baseModule/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
 import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+import { procedureTypeNames } from "@/lib/shared/components/procedures/constants";
 
 export default function TaskMetricsPage(
   props: Readonly<{
-    params: {
-      businessModuleName: string;
-      procedureType: ApiProcedureType;
-    };
+    params: { businessModuleName: string; procedureType: ApiProcedureType };
   }>,
 ) {
-  const timeRangeEnd = endOfToday();
-
-  const [selectedTimeRange, _setSelectedTimeRange] = useState(12);
-
-  const timeRangeStart = lastXMonthsInDate(timeRangeEnd, selectedTimeRange);
-
-  const procedureMetrics = useTaskMetricsQuery({
-    businessModuleName: props.params.businessModuleName,
-    procedureType: props.params.procedureType,
-    timeRangeStart,
-    timeRangeEnd,
-  });
-
   return (
     <StickyToolbarLayout
       toolbar={
         <Toolbar
-          title={`Aufgabenkennzahlen: ${props.params.procedureType}`}
+          title={`Aufgabenkennzahlen: ${procedureTypeNames[props.params.procedureType]}`}
           backHref={routes.metrics.index}
         />
       }
     >
       <MainContentLayout>
-        {procedureMetrics.taskMetrics.join()}
+        <TaskMetricsDisplay
+          businessModuleName={props.params.businessModuleName}
+          procedureType={props.params.procedureType}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
index f835fcf3db2bde002eb5e0eeb4dd0c03b5768a39..2a86516df395985382066c4f762df25c6b5df09a 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
@@ -352,6 +352,7 @@ function mapToRequest(
       ),
       promotionTherapyAndAidInfo: mapPromotionTherapyAndAidInfo(
         formValues.promotionTherapyAndAidInfo,
+        formValues.promotionBeforeSchoolEntry,
       ),
       personalConspicuities: mapOptionalValue(formValues.personalConspicuities),
     },
@@ -446,6 +447,7 @@ function mapPromotionBeforeSchoolEntry(
 
 function mapPromotionTherapyAndAidInfo(
   values: PromotionTherapyAndAidInfoValues,
+  promotionBeforeSchoolEntryValues: PromotionBeforeSchoolEntryValues,
 ) {
   return {
     visionImpairment: mapOptionalValue(values.visionImpairment),
@@ -454,12 +456,24 @@ function mapPromotionTherapyAndAidInfo(
     spectaclesSince: mapOptionalDate(values.spectaclesSince),
     visionSchoolSince: mapOptionalDate(values.visionSchoolSince),
     hearingAid: mapOptionalValue(values.hearingAid),
-    speechTherapyStart: mapOptionalDate(values.speechTherapyStart),
-    speechTherapyEnd: mapOptionalDate(values.speechTherapyEnd),
-    ergoTherapyStart: mapOptionalDate(values.ergoTherapyStart),
-    ergoTherapyEnd: mapOptionalDate(values.ergoTherapyEnd),
-    physioTherapyStart: mapOptionalDate(values.physioTherapyStart),
-    physioTherapyEnd: mapOptionalDate(values.physioTherapyEnd),
+    speechTherapyStart: promotionBeforeSchoolEntryValues.speechTherapy
+      ? mapOptionalDate(values.speechTherapyStart)
+      : undefined,
+    speechTherapyEnd: promotionBeforeSchoolEntryValues.speechTherapy
+      ? mapOptionalDate(values.speechTherapyEnd)
+      : undefined,
+    ergoTherapyStart: promotionBeforeSchoolEntryValues.ergotherapy
+      ? mapOptionalDate(values.ergoTherapyStart)
+      : undefined,
+    ergoTherapyEnd: promotionBeforeSchoolEntryValues.ergotherapy
+      ? mapOptionalDate(values.ergoTherapyEnd)
+      : undefined,
+    physioTherapyStart: promotionBeforeSchoolEntryValues.physiotherapy
+      ? mapOptionalDate(values.physioTherapyStart)
+      : undefined,
+    physioTherapyEnd: promotionBeforeSchoolEntryValues.physiotherapy
+      ? mapOptionalDate(values.physioTherapyEnd)
+      : undefined,
     additionalTherapies: mapOptionalValue(values.additionalTherapies),
   };
 }
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
index f2a78d214b336d8e849c3bd87f377ac84dfad5b0..d100f5e33319ffd0a182bb5fb05ce68e8b7f2bcc 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
@@ -15,6 +15,7 @@ import { PropsWithChildren, useState } from "react";
 import { SchoolEntryProcedurePageProps } from "@/app/(businessModules)/school-entry/procedures/[id]/layout";
 import { useSchoolEntryApi } from "@/lib/businessModules/schoolEntry/api/clients";
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
+import { useGetProcedure } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryApi";
 import { RequiredProcedureDataDialog } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/RequiredProcedureDataModal";
 import { MedicalReportSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar";
 import { SchoolInfoLetterSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar";
@@ -64,6 +65,7 @@ export default function SchoolEntryExaminationLayout(
   );
   const procedureId = props.params.id;
   const navItems = buildNavItems(procedureId);
+  const procedureDetails = useGetProcedure(procedureId).data;
 
   return (
     <PageGrid>
@@ -80,13 +82,14 @@ export default function SchoolEntryExaminationLayout(
               ))}
             </SidePanelNav>
           </SidePanel>
-          {(medicalReportEnabled || schoolInfoLetterEnabled) && (
-            <CreateReportsPanel
-              procedureId={procedureId}
-              showMedicalReportButton={medicalReportEnabled}
-              showSchoolInfoLetterButton={schoolInfoLetterEnabled}
-            />
-          )}
+          {(medicalReportEnabled || schoolInfoLetterEnabled) &&
+            !procedureDetails.isClosed && (
+              <CreateReportsPanel
+                procedureId={procedureId}
+                showMedicalReportButton={medicalReportEnabled}
+                showSchoolInfoLetterButton={schoolInfoLetterEnabled}
+              />
+            )}
         </Stack>
       </Grid>
     </PageGrid>
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageNotification.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageNotification.tsx
index 9bce0ece5ee2933651478307373e7eea5354d186..1a8f471697b22c9f250802cab06e765c51393379 100644
--- a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageNotification.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageNotification.tsx
@@ -6,13 +6,21 @@
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
-import SendIcon from "@mui/icons-material/Send";
-import { Avatar, Card, IconButton, Stack, Typography } from "@mui/joy";
+import CloseIcon from "@mui/icons-material/Close";
+import SendOutlinedIcon from "@mui/icons-material/SendOutlined";
+import { Box, Card, IconButton, Stack, Typography } from "@mui/joy";
+import { de } from "date-fns/locale";
 import { Formik } from "formik";
 import { User } from "matrix-js-sdk/lib/matrix";
 
+import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
+import { useGetUsersPresence } from "@/lib/businessModules/chat/shared/hooks/useGetUsersPresence";
 import { useSendMessage } from "@/lib/businessModules/chat/shared/hooks/useSendMessage";
-import { Message } from "@/lib/businessModules/chat/shared/types";
+import { Message, Presence } from "@/lib/businessModules/chat/shared/types";
+import {
+  getStatusColor,
+  markAllMessagesAsRead,
+} from "@/lib/businessModules/chat/shared/utils";
 import { formatDateTimeRangeToNow } from "@/lib/shared/helpers/dateTime";
 
 export function MessageNotification({
@@ -23,30 +31,99 @@ export function MessageNotification({
   sender: User | null;
 }) {
   const { sendMessage } = useSendMessage();
+  const usersPresence = useGetUsersPresence();
+  const { matrixClient } = useChatClientContext();
+  const isPrivateChat =
+    matrixClient
+      .getRoom(message.roomId)
+      ?.getMembers()
+      .filter((member) => member.userId !== matrixClient.getUserId()).length ==
+    1;
 
   return (
-    <Card variant="soft" data-testid="notification" size="sm">
-      <Stack direction="row" spacing={1}>
-        <Stack direction="row" alignItems="start">
-          <Avatar src={sender?.avatarUrl} />
-        </Stack>
-        <Stack width="100%">
-          <Stack
-            direction="row"
-            justifyContent="space-between"
-            alignItems="center"
-            spacing={1}
-          >
-            <Typography level="body-md" fontWeight={600}>
-              {sender?.displayName}
-            </Typography>
-            <Typography level="body-xs" color="neutral" flexShrink={0}>
-              {message.timestamp
-                ? formatDateTimeRangeToNow(message.timestamp)
-                : ""}
-            </Typography>
+    <Card
+      variant="plain"
+      data-testid="notification"
+      size="sm"
+      slotProps={{
+        root: {
+          sx: {
+            backgroundColor: "common.white",
+            marginInline: 0,
+            p: 0,
+          },
+        },
+      }}
+    >
+      <Stack direction="row">
+        <Stack width="100%" justifyContent="space-between">
+          <Stack direction="row" alignItems="center">
+            <Box
+              display="flex"
+              flexDirection="row"
+              sx={{
+                width: "100%",
+                boxSizing: "content-box",
+                alignItems: "center",
+              }}
+            >
+              {usersPresence && isPrivateChat && (
+                <Box
+                  sx={{
+                    width: "0.625rem",
+                    height: "0.625rem",
+                    borderRadius: "100%",
+                    backgroundColor: getStatusColor(
+                      sender?.presence as Presence,
+                    ),
+                    marginRight: 0.8,
+                  }}
+                ></Box>
+              )}
+              <Typography
+                level="title-md"
+                sx={{
+                  fontWeight: "bold",
+                  height: "1.5rem",
+                  maxWidth: "15rem",
+                  textOverflow: "ellipsis",
+                }}
+              >
+                {isPrivateChat
+                  ? sender?.displayName
+                  : matrixClient?.getRoom(message.roomId)?.name}
+              </Typography>
+            </Box>
+            <IconButton
+              aria-label="Schließen"
+              onClick={() =>
+                markAllMessagesAsRead({
+                  matrixClient: matrixClient,
+                  roomId: message.roomId,
+                })
+              }
+              color="primary"
+            >
+              <CloseIcon />
+            </IconButton>
           </Stack>
-          <Typography mb={0.5}>{message.content}</Typography>
+          <Box display="flex" flexDirection="row">
+            <Typography mb={0.5}>
+              {isPrivateChat
+                ? message.content
+                : `${sender?.displayName}: ${message.content}`}
+              <Typography
+                component="span"
+                flexShrink={0}
+                paddingLeft="4px"
+                sx={{ color: "text.tertiary" }}
+              >
+                {message.timestamp
+                  ? formatDateTimeRangeToNow(message.timestamp, { locale: de })
+                  : ""}
+              </Typography>
+            </Typography>
+          </Box>
           <Formik
             initialValues={{ messageValue: "" }}
             onSubmit={({ messageValue }) => {
@@ -58,16 +135,24 @@ export function MessageNotification({
             <FormPlus>
               <InputField
                 label=""
+                placeholder="Antworten"
                 type="text"
                 name="messageValue"
                 endDecorator={
-                  <IconButton aria-label="Schaltfläche Senden" type="submit">
-                    <SendIcon />
+                  <IconButton
+                    aria-label="Schaltfläche Senden"
+                    type="submit"
+                    color="primary"
+                  >
+                    <SendOutlinedIcon />
                   </IconButton>
                 }
                 sx={{
-                  "& input": {
-                    width: "100%",
+                  ".MuiInput-endDecorator": {
+                    paddingRight: "8px",
+                  },
+                  ".MuiInput-root": {
+                    height: "2.75rem",
                   },
                 }}
               />
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
index dcd90aad6288eab5a9781a0d311f907618132a0d..f9f9a3e89d5ca290838f6d25a6fd7904d838305a 100644
--- a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
@@ -4,12 +4,12 @@
  */
 
 import { useNavigation } from "@eshg/lib-portal/components/navigation/NavigationContext";
-import { Button } from "@mui/joy";
+import OpenInNew from "@mui/icons-material/OpenInNew";
+import { Button, Divider, Stack } from "@mui/joy";
 
 import { routes } from "@/lib/baseModule/shared/routes";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 
 import { MessagesSidebarContent } from "./MessagesSidebarContent";
@@ -17,26 +17,29 @@ import { MessagesSidebarContent } from "./MessagesSidebarContent";
 export function MessagesSidebar() {
   const { chatSidebar } = useChat();
   const { tryNavigate } = useNavigation();
+
   return (
     <Sidebar
       open={chatSidebar.isOpen}
       onClose={chatSidebar.close}
       zIndex={"headerSidebar"}
     >
-      <SidebarContent title="Nachrichten">
+      <SidebarContent title="Ungelesene Chats">
         <MessagesSidebarContent />
       </SidebarContent>
-      <SidebarActions>
+      <Stack sx={{ paddingTop: 3 }} data-testid="sidebarActions">
+        <Divider sx={{ marginBottom: 3, marginInline: -3, marginTop: -3 }} />
         <Button
+          sx={{ alignSelf: "end" }}
           onClick={() => {
             chatSidebar.close();
             tryNavigate(routes.chat as string);
           }}
-          sx={{ alignSelf: "end" }}
+          endDecorator={<OpenInNew />}
         >
-          Zum Chat
+          Chatbereich
         </Button>
-      </SidebarActions>
+      </Stack>
     </Sidebar>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
index 9f3f6c653b5ba0d21011e7c501f5d58d6aa573f7..2367f02347cff3151ba1e2900b94f8b81d57d18b 100644
--- a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
@@ -3,26 +3,22 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import DraftsIcon from "@mui/icons-material/Drafts";
-import { Stack, Typography } from "@mui/joy";
+import { Divider, Stack, Typography } from "@mui/joy";
 
 import { MessageNotification } from "@/lib/baseModule/components/layout/messagesSidebar/MessageNotification";
+import { NoMessagesIllustration } from "@/lib/businessModules/chat/assets/NoMessagesIllustration";
 import { ChatNoAccessAlert } from "@/lib/businessModules/chat/components/ChatNoAccessAlert";
+import { GhostButton } from "@/lib/businessModules/chat/components/GhostButton";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { useMatrixClient } from "@/lib/businessModules/chat/shared/hooks/useMatrixClient";
 import { useNewMessages } from "@/lib/businessModules/chat/shared/hooks/useNewMessages";
+import { allMessagesRead } from "@/lib/businessModules/chat/shared/utils";
 
 function NoMessagesInfo() {
   return (
-    <Stack gap={3} alignItems={"center"}>
-      <DraftsIcon
-        sx={{
-          marginTop: { xxs: 5, sm: 10 },
-          fontSize: { xxs: 80, sm: 128 },
-        }}
-      />
-      <Typography level="h4" component="h2">
-        Aktuell nichts Neues
-      </Typography>
+    <Stack alignItems="center" justifyContent="center" sx={{ height: "100%" }}>
+      <NoMessagesIllustration sx={{ width: "400px", height: "auto" }} />
+      <Typography>Keine neuen Nachrichten.</Typography>
     </Stack>
   );
 }
@@ -30,6 +26,7 @@ function NoMessagesInfo() {
 export function MessagesSidebarContent() {
   const { userSettings } = useChat();
   const { newMessages } = useNewMessages();
+  const matrixClient = useMatrixClient();
 
   if (!userSettings.chatUsageEnabled) {
     return <ChatNoAccessAlert />;
@@ -39,14 +36,26 @@ export function MessagesSidebarContent() {
     return <NoMessagesInfo />;
   }
   return (
-    <Stack sx={{ marginTop: 3 }} gap={2}>
-      {newMessages.map((notification) => (
-        <MessageNotification
-          key={notification.id}
-          message={notification}
-          sender={notification.sender}
-        />
-      ))}
+    <Stack>
+      <GhostButton
+        onClick={() => {
+          if (matrixClient) {
+            allMessagesRead(matrixClient, newMessages);
+          }
+        }}
+      >
+        Alle als gelesen markieren
+      </GhostButton>
+      <Divider sx={{ marginBottom: 3, marginInline: -3, marginTop: 4 }} />
+      <Stack gap={5} sx={{ paddingBottom: -3 }}>
+        {newMessages.map((notification) => (
+          <MessageNotification
+            key={notification.id}
+            message={notification}
+            sender={notification.sender}
+          />
+        ))}
+      </Stack>
     </Stack>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
index 128eb37cf2d744e869904ba03062990674623ac5..13d359b564c10e66f3bf3f8b69bcd104cdea40ce 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
@@ -16,7 +16,7 @@ import {
 } from "@mui/icons-material";
 import { Stack, Typography } from "@mui/joy";
 import { endOfToday } from "date-fns";
-import { useState } from "react";
+import { startTransition, useState } from "react";
 import { unique } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
@@ -68,7 +68,11 @@ export function ProcedureMetricsDisplay() {
       <TimeRangeSelect
         optionsInMonths={[1, 3, 6, 12]}
         selectedTimeRange={selectedTimeRange}
-        setSelectedTimeRange={setSelectedTimeRange}
+        setSelectedTimeRange={(value) =>
+          startTransition(() => {
+            setSelectedTimeRange(value);
+          })
+        }
       />
       <Stack role="list" direction="row" flexWrap="wrap" gap={2}>
         <FlashCard
@@ -105,13 +109,13 @@ export function ProcedureMetricsDisplay() {
       <Stack gap={5}>
         {uniqueBusinessModules.map((businessModule) => (
           <Stack key={businessModuleNames[businessModule]} gap={2}>
-            <Typography
-              level={"body-lg"}
-              key={businessModuleNames[businessModule]}
+            <TableSheet
+              title={
+                <Typography level="h3" component="h2">
+                  {businessModuleNames[businessModule]}
+                </Typography>
+              }
             >
-              {businessModuleNames[businessModule]}
-            </Typography>
-            <TableSheet>
               <DataTable
                 data={procedureMetrics.filter(
                   (procedure) => procedure.businessModule === businessModule,
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8fae0bda7ed1ba7e07d99c3a317058568f7ada7d
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiProcedureType } from "@eshg/employee-portal-api/base";
+import {
+  CheckOutlined,
+  HourglassEmptyOutlined,
+  RocketLaunchOutlined,
+} from "@mui/icons-material";
+import { Stack, Typography } from "@mui/joy";
+import { endOfToday } from "date-fns";
+import { startTransition, useState } from "react";
+
+import { useTaskMetricsQuery } from "@/lib/baseModule/api/queries/taskMetrics";
+import { TimeRangeSelect } from "@/lib/baseModule/components/procedureMetrics/TimeRangeSelect";
+import { lastXMonthsInDate } from "@/lib/baseModule/components/procedureMetrics/rangeSelectHelper";
+import { FlashCard } from "@/lib/shared/components/cards/FlashCard";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+
+import { formatOptionalDuration } from "./formatOptionalDuration";
+import { slowestAndFastestTasksColumns } from "./slowestAndFastestColumns";
+import { tasksColumns } from "./taskColumns";
+
+export function TaskMetricsDisplay(props: {
+  businessModuleName: string;
+  procedureType: ApiProcedureType;
+}) {
+  const timeRangeEnd = endOfToday();
+
+  const [selectedTimeRange, setSelectedTimeRange] = useState(12);
+
+  const timeRangeStart = lastXMonthsInDate(timeRangeEnd, selectedTimeRange);
+
+  const taskMetrics = useTaskMetricsQuery({
+    businessModuleName: props.businessModuleName,
+    procedureType: props.procedureType,
+    timeRangeStart,
+    timeRangeEnd,
+  });
+
+  return (
+    <Stack gap={3}>
+      <TimeRangeSelect
+        optionsInMonths={[1, 3, 6, 12]}
+        selectedTimeRange={selectedTimeRange}
+        setSelectedTimeRange={(value) =>
+          startTransition(() => {
+            setSelectedTimeRange(value);
+          })
+        }
+      />
+      <Stack role="list" direction="row" flexWrap="wrap" gap={2}>
+        <FlashCard
+          color={"primary"}
+          title="Geschlossene Vorgänge"
+          figure={`${taskMetrics.closedProcedureCount}`}
+          icon={<CheckOutlined fontSize="xl4" />}
+        />
+        <FlashCard
+          color={"danger"}
+          title="Langsamster Vorgang"
+          figure={`${formatOptionalDuration(taskMetrics.slowestProcedures[0]?.duration)}`}
+          icon={<HourglassEmptyOutlined fontSize="xl4" />}
+        />
+        <FlashCard
+          color={"success"}
+          title="Schnellster Vorgang"
+          figure={`${formatOptionalDuration(taskMetrics.fastestProcedures[0]?.duration)}`}
+          icon={<RocketLaunchOutlined fontSize="xl4" />}
+        />
+      </Stack>
+      <Stack gap={3}>
+        <TableSheet
+          title={
+            <Stack gap={3} marginBottom={1}>
+              <Typography level="h3" component="h2">
+                Aufgaben
+              </Typography>
+              <Typography>
+                Auftreten der Aufgaben in abgeschlossenen Vorgängen.
+              </Typography>
+            </Stack>
+          }
+        >
+          <DataTable
+            data={taskMetrics.taskMetrics}
+            columns={tasksColumns}
+            sorting={{
+              manualSorting: false,
+            }}
+          />
+        </TableSheet>
+
+        <TableSheet
+          title={
+            <Stack marginBottom={1}>
+              <Typography level="h3" component="h2">
+                Schnellste Vorgänge
+              </Typography>
+            </Stack>
+          }
+        >
+          <DataTable
+            data={taskMetrics.fastestProcedures}
+            columns={slowestAndFastestTasksColumns}
+            sorting={{
+              manualSorting: false,
+            }}
+          />
+        </TableSheet>
+
+        <TableSheet
+          title={
+            <Stack marginBottom={1}>
+              <Typography level="h3" component="h2">
+                Langsamste Vorgänge
+              </Typography>
+            </Stack>
+          }
+        >
+          <DataTable
+            data={taskMetrics.slowestProcedures}
+            columns={slowestAndFastestTasksColumns}
+            sorting={{
+              manualSorting: false,
+            }}
+          />
+        </TableSheet>
+      </Stack>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/formatOptionalDuration.ts b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/formatOptionalDuration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62056427697613a7ae6b660a0795c2211f14e2e4
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/formatOptionalDuration.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { isDefined } from "remeda";
+
+import { formatDurationRounded } from "@/lib/shared/helpers/dateTime";
+
+export function formatOptionalDuration(value: string | undefined) {
+  return isDefined(value) ? formatDurationRounded(value) : "-";
+}
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..719e2b7554f92852a2511d810053b51e989ff377
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiProcedureWithDuration } from "@eshg/employee-portal-api/base";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createColumnHelper } from "@tanstack/react-table";
+
+import { formatOptionalDuration } from "./formatOptionalDuration";
+
+const columnHelper = createColumnHelper<ApiProcedureWithDuration>();
+
+const meta = {
+  width: "6rem",
+};
+
+export const slowestAndFastestTasksColumns = [
+  columnHelper.accessor("createdAt", {
+    header: "Erstellt am",
+    cell: (props) => formatDate(props.getValue()),
+    meta,
+  }),
+  columnHelper.accessor("duration", {
+    header: "Durchschnittliche Dauer",
+    cell: (props) => {
+      return formatOptionalDuration(props.getValue());
+    },
+    meta,
+  }),
+];
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/taskColumns.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/taskColumns.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a68f9d3df3a1601e435658f7d287fcf6e055ff34
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/taskColumns.tsx
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiTaskMetric } from "@eshg/employee-portal-api/base";
+import { createColumnHelper } from "@tanstack/react-table";
+
+import { taskTypeNames } from "@/lib/shared/components/procedures/constants";
+
+import { formatOptionalDuration } from "./formatOptionalDuration";
+
+const columnHelper = createColumnHelper<ApiTaskMetric>();
+
+const meta = {
+  width: "6rem",
+};
+
+export const tasksColumns = [
+  columnHelper.accessor("taskType", {
+    header: "Bezeichnung",
+    cell: (props) => taskTypeNames[props.getValue()],
+    meta: {
+      width: "10rem",
+    },
+  }),
+  columnHelper.accessor("averageDuration", {
+    header: "Durchschnittliche Dauer",
+    cell: (props) => {
+      return formatOptionalDuration(props.getValue());
+    },
+    meta,
+  }),
+  columnHelper.accessor("noOccurrencesCount", {
+    header: "Kein Auftreten",
+    meta,
+  }),
+  columnHelper.accessor("oneOccurrenceCount", {
+    header: "Auftreten: 1x",
+    meta,
+  }),
+  columnHelper.accessor("twoOccurrencesCount", {
+    header: "Auftreten: 2x",
+    meta,
+  }),
+  columnHelper.accessor("moreThanTwoOccurrencesCount", {
+    header: "Auftreten: >2",
+    meta,
+  }),
+];
diff --git a/employee-portal/src/lib/businessModules/chat/assets/NoMessagesIllustration.tsx b/employee-portal/src/lib/businessModules/chat/assets/NoMessagesIllustration.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6ff1b72a600f70e668d8e0a82e8f8ab73caf07ee
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/assets/NoMessagesIllustration.tsx
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { SvgIcon, SvgIconProps } from "@mui/joy";
+
+export function NoMessagesIllustration(props: SvgIconProps) {
+  return (
+    <SvgIcon {...props}>
+      <svg
+        width="441"
+        height="441"
+        viewBox="0 0 441 441"
+        fill="none"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <rect
+          width="440"
+          height="440"
+          transform="translate(0.744141 0.5)"
+          fill="white"
+        />
+        <rect
+          x="39.1875"
+          y="31.9434"
+          width="363.113"
+          height="363.113"
+          rx="181.557"
+          fill="#F0F4F8"
+        />
+        <path
+          d="M151.897 125.881H303.614C314.045 125.881 322.579 134.415 322.579 144.846V315.527L284.65 277.598H151.897C141.467 277.598 132.933 269.064 132.933 258.633V144.846C132.933 134.415 141.467 125.881 151.897 125.881Z"
+          fill="#CDD7E1"
+        />
+        <path
+          d="M106.911 171.215L115.642 204.77L149.196 213.5L115.642 222.231L106.911 255.786L98.1797 222.231L64.625 213.5L98.1797 204.77L106.911 171.215Z"
+          fill="#DDE7EE"
+        />
+        <path
+          d="M261.416 315.527L268.005 340.85L293.328 347.439L268.005 354.029L261.416 379.352L254.827 354.029L229.504 347.439L254.827 340.85L261.416 315.527Z"
+          fill="white"
+        />
+        <path
+          d="M258.235 58.957L262.774 76.3989L280.215 80.9373L262.774 85.4757L258.235 102.918L253.697 85.4757L236.255 80.9373L253.697 76.3989L258.235 58.957Z"
+          fill="white"
+        />
+        <path
+          d="M183.867 204.446L219.711 236.309L275.142 168.195"
+          stroke="white"
+          strokeWidth="9"
+          strokeLinecap="square"
+        />
+      </svg>
+    </SvgIcon>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
index 7542ade9f5205127c6abda22fd1fda9635b97f41..44f12d7ac76607a05476f3ff1161a395a7d0a558 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
@@ -7,29 +7,30 @@ import Box from "@mui/joy/Box";
 import Sheet from "@mui/joy/Sheet";
 import Stack from "@mui/joy/Stack";
 import Typography from "@mui/joy/Typography";
-import { format, isToday } from "date-fns";
-import { User } from "matrix-js-sdk/lib/matrix";
 import { ReactNode } from "react";
+import { isEmpty } from "remeda";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { ReadingReceipt } from "@/lib/businessModules/chat/components/chatPanel/ReadingReceipt";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { Message } from "@/lib/businessModules/chat/shared/types";
-import { formatDateForChat } from "@/lib/businessModules/chat/shared/utils";
+import { formatChatDate } from "@/lib/businessModules/chat/shared/utils";
 
 interface ChatBubbleProps {
   message: Message;
   variant: "sent" | "received";
   loggedInUserId: string;
-  receiptUsers: (User | null)[];
+  lastReadMessageIndexes: number[];
+  index: number;
 }
 
 export function ChatBubble({
   variant,
   message,
   loggedInUserId,
-  receiptUsers,
+  lastReadMessageIndexes = [],
+  index,
 }: Readonly<ChatBubbleProps>) {
   const { matrixClient } = useChatClientContext();
   const { userSettings } = useChat();
@@ -40,6 +41,14 @@ export function ChatBubble({
       return user?.displayName;
     })
     .filter((item) => !!item) as string[];
+  const hasNoReceipts = isEmpty(lastReadMessageIndexes);
+
+  // Messages are sorted from newest to oldest.
+  // Here, we compare the index to check if it is greater than the last read index.
+  // This means that the message is older than the read ones, so it must have been read.
+  const isMessageRead = lastReadMessageIndexes.some(
+    (readIndex) => index >= readIndex,
+  );
 
   return (
     <Stack direction="column" alignItems="flex-start">
@@ -59,9 +68,7 @@ export function ChatBubble({
         </Typography>
         {message.timestamp && (
           <Typography textColor="text.secondary" sx={{ fontSize: "0.875rem" }}>
-            {isToday(message.timestamp)
-              ? `${format(message.timestamp, "HH:MM")} Uhr`
-              : formatDateForChat(message.timestamp)}
+            {formatChatDate(message.timestamp)}
           </Typography>
         )}
       </Stack>
@@ -100,7 +107,7 @@ export function ChatBubble({
         {isSent && (
           <ReadingReceipt
             isReadReceiptEnabled={userSettings.showReadConfirmation}
-            isRead={receiptUsers?.length > 0}
+            isRead={hasNoReceipts ? false : isMessageRead}
           />
         )}
       </Box>
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
index 5c6a19431571d3e9fc63b7faa82ea7916b15987e..e8dd50d414965c9fc966d5394b69ccf4eaf34fa0 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
@@ -6,44 +6,55 @@
 import { Box, Divider, List, ListItem, Typography, useTheme } from "@mui/joy";
 import { isSameDay, startOfDay } from "date-fns";
 import { Fragment, useEffect, useRef } from "react";
+import { isEmpty, isNonNullish } from "remeda";
 
 import { ChatIllustrationBackground } from "@/lib/businessModules/chat/components/ChatIllustrationBackground";
 import { ChatBubble } from "@/lib/businessModules/chat/components/chatPanel/ChatBubble";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { useReadConfirmation } from "@/lib/businessModules/chat/shared/hooks/useReadConfirmation";
-import { useRoomMessages } from "@/lib/businessModules/chat/shared/hooks/useRoomMessages";
 import { useTyping } from "@/lib/businessModules/chat/shared/hooks/useTyping";
 import {
   Message,
   RoomWithCommunicationType,
 } from "@/lib/businessModules/chat/shared/types";
-import {
-  formatUserReceipts,
-  getDayLabel,
-} from "@/lib/businessModules/chat/shared/utils";
+import { getDayLabel } from "@/lib/businessModules/chat/shared/utils";
 
 interface ChatMessagesProps {
   room: RoomWithCommunicationType;
+  messages: Message[];
 }
 
-export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
+export function ChatMessages({ room, messages }: Readonly<ChatMessagesProps>) {
   const {
     userSettings: { showReadConfirmation },
   } = useChat();
   const { matrixClient } = useChatClientContext();
-  const loggedInUserId = matrixClient.getUserId();
+  const loggedInUserId = matrixClient.getUserId() ?? "";
   const { readConfirmationsPerRoom } =
     useReadConfirmation(showReadConfirmation);
-  const confirmationsArr = formatUserReceipts(
-    readConfirmationsPerRoom[room.room.roomId],
+  const roomReceipts = readConfirmationsPerRoom[room.room.roomId];
+  // Here we filter out the logged-in user ID.
+  const { [loggedInUserId]: _, ...readConfirmationFromOtherUsers } =
+    roomReceipts ?? {};
+  const lastReadMessageIds = Object.values(readConfirmationFromOtherUsers)?.map(
+    ({ eventId }) => eventId,
   );
+  const lastReadIndexes = messages
+    .map(({ id }, index) =>
+      lastReadMessageIds.includes(id) ? index : undefined,
+    )
+    .filter((item) => isNonNullish(item));
+  const initialReadIndexes = messages
+    .map(({ readReceipts }, index) =>
+      readReceipts && !isEmpty(readReceipts) ? index : undefined,
+    )
+    .filter((item) => isNonNullish(item));
   const {
     userSettings: { showTypingNotification },
   } = useChat();
   const { typingUsersList } = useTyping(showTypingNotification);
   const typingUsers = typingUsersList[room.room.roomId];
-  const { messages } = useRoomMessages();
   const theme = useTheme();
   const messagesWrapperRef = useRef<HTMLUListElement>(null);
   const wrapperRef = useRef<HTMLDivElement>(null);
@@ -77,12 +88,6 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
         {messages?.map((message: Message, index: number) => {
           const isYou = message.sender?.userId === loggedInUserId;
 
-          const confirmationIds = confirmationsArr?.[message.id];
-
-          const receiptUsers =
-            showReadConfirmation && Array.isArray(confirmationIds)
-              ? confirmationIds.map((userId) => matrixClient.getUser(userId))
-              : [];
           const nextMessage = messages[index + 1];
           const shouldShowDivider =
             index !== messages.length - 1 &&
@@ -108,9 +113,10 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
                   variant={isYou ? "sent" : "received"}
                   loggedInUserId={loggedInUserId}
                   message={message}
-                  receiptUsers={receiptUsers.filter(
-                    (user) => user?.userId !== loggedInUserId,
-                  )}
+                  lastReadMessageIndexes={
+                    [...initialReadIndexes, ...lastReadIndexes] as number[]
+                  }
+                  index={index}
                 />
               </ListItem>
               {shouldShowDivider && message.timestamp && (
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
index acecc0bc70aa9cac54950602de0ab5910a7837e0..564cd474927df09fea328cb662c39883f3d03a69 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
@@ -47,7 +47,7 @@ export function ChatPanel({
     userSettings: { showTypingNotification },
   } = useChat();
   const { handleUserTyping } = useTyping(showTypingNotification);
-  const { handleSendMessage } = useRoomMessages();
+  const { handleSendMessage, messages } = useRoomMessages();
   const [userList, setUserList] = useState<
     (ApiUser & { department?: string })[] | undefined
   >();
@@ -143,7 +143,7 @@ export function ChatPanel({
             flexDirection: "column",
           }}
         >
-          <ChatMessages room={roomWithCommunicationType} />
+          <ChatMessages room={roomWithCommunicationType} messages={messages} />
           <MessageInput
             handleUserTyping={handleUserTyping}
             selectedRoomId={roomId}
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
index f6c299bfde59e4bd7abf6774d1ff7cea965d6bb7..ed04aa8038ae4e6fcc39c499ab5dbd11c687b393 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
@@ -16,6 +16,7 @@ import { InputComponent } from "@/lib/businessModules/chat/components/chatPanel/
 import { ChatPanelView } from "@/lib/businessModules/chat/shared/enums";
 import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import { useCreateNewChat } from "@/lib/businessModules/chat/shared/hooks/useCreateNewChat";
+import { useRoomMessages } from "@/lib/businessModules/chat/shared/hooks/useRoomMessages";
 import { useSendMessage } from "@/lib/businessModules/chat/shared/hooks/useSendMessage";
 import { ApiUser } from "@/lib/businessModules/chat/shared/types";
 
@@ -36,6 +37,7 @@ export function NewDirectChat({
 }: Readonly<NewDirectChatProps>) {
   const { createNewDirectMessage, findExisingRoom } = useCreateNewChat();
   const { sendMessage } = useSendMessage();
+  const { messages } = useRoomMessages();
   const theme = useTheme();
   const { setRoomIdParam } = useChatSearchParams();
 
@@ -127,7 +129,7 @@ export function NewDirectChat({
               </Box>
 
               {isObjectType(existingChat) && existingChat?.room.roomId ? (
-                <ChatMessages room={existingChat} />
+                <ChatMessages room={existingChat} messages={messages} />
               ) : (
                 <ChatIllustrationBackground />
               )}
diff --git a/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx b/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
index 9b263a958e4f67819b7009815bec0e50c7232040..21a6bca67f7ff7037a5ece6dda43f8059ce0c792 100644
--- a/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
@@ -14,7 +14,7 @@ import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClie
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
 import { RoomLastMessage } from "@/lib/businessModules/chat/shared/types";
 import {
-  convertMessageTimestamp,
+  formatChatDate,
   getMemberAvatarUrl,
   getRoomAvatarUrl,
   getRoomLastMessage,
@@ -34,7 +34,7 @@ export function RoomListItem({
   const { matrixClient, unreadNotificationsPerRoom } = useChatClientContext();
   const [lastMessage, setLastMessage] = useState<RoomLastMessage>();
 
-  const parsedDate = convertMessageTimestamp(lastMessage?.timestamp);
+  const parsedDate = formatChatDate(lastMessage?.timestamp);
   const unreadNotifications = unreadNotificationsPerRoom[room.roomId];
 
   // TO DO - finish notification feature
diff --git a/employee-portal/src/lib/businessModules/chat/components/secureBackup/BackupSetupView.tsx b/employee-portal/src/lib/businessModules/chat/components/secureBackup/BackupSetupView.tsx
index 77bd92cd38bf118f027815f6e44c4d68bf148012..bfed28a47e435c90c0551442914fc00894a0afb6 100644
--- a/employee-portal/src/lib/businessModules/chat/components/secureBackup/BackupSetupView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/secureBackup/BackupSetupView.tsx
@@ -21,18 +21,19 @@ export interface SecureBackupContent {
 
 const content = {
   [ClientState.CreateBackupKey]: {
-    header: "Set up Secure Backup",
-    subheader: "Set up a secure backup to be able to use chat.",
+    header: "Richten Sie ein Sicherheitsbackup ein",
+    subheader:
+      "Richten Sie ein Sicherheitsbackup ein um die Chatfunktion zu nutzen",
     description: [
-      "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
-      "Enter a Security Phrase only you know, as it's used to safeguard your data. To be secure, you shouldn't re-use your account password.",
+      "Schützen Sie sich vor dem Verlust des Zugriffs auf verschlüsselte Nachrichten und Daten, indem Sie die Sicherheitsschlüssel auf Ihrem Server sichern.",
+      "Geben Sie ein Passwort ein, das nur Sie kennen, da es zum Schutz Ihrer Daten verwendet wird. Aus Sicherheitsgründen sollten Sie Ihr GA-Lotse Benutzerpasswort nicht wieder verwenden.",
     ],
   },
   [ClientState.RestoreBackupKey]: {
-    header: "Verify this device",
-    subheader: "Verify this device to be able to use chat.",
+    header: "Bestätigen sie dieses Endgerät",
+    subheader: "Bestätigen sie dieses Endgerät um die Chatfunktion zu nutzen",
     description: [
-      "Verify your identity to access encrypted messages and prove your identity to others.",
+      "Bestätigen Sie Ihre Identität, um auf verschlüsselte Nachrichten zuzugreifen und Ihre Identität gegenüber anderen zu bestätigen.",
     ],
   },
 };
diff --git a/employee-portal/src/lib/businessModules/chat/components/secureBackup/CreateBackupSidebar.tsx b/employee-portal/src/lib/businessModules/chat/components/secureBackup/CreateBackupSidebar.tsx
index 6892ecd6217e3cb33442e8c6dbd6dbb43fa74aba..620a318da4764f1fc3028bc7debf97771c4e528e 100644
--- a/employee-portal/src/lib/businessModules/chat/components/secureBackup/CreateBackupSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/secureBackup/CreateBackupSidebar.tsx
@@ -5,7 +5,6 @@
 
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
-import { validateLength } from "@eshg/lib-portal/helpers/validators";
 import { CheckCircleOutline, RadioButtonUnchecked } from "@mui/icons-material";
 import { Stack, Typography } from "@mui/joy";
 import { Formik } from "formik";
@@ -29,9 +28,9 @@ import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 import {
-  getPassphraseValidityInfo,
-  validatePassphrase,
-} from "@/lib/shared/helpers/validatePassphrase";
+  getPasswordValidityInfo,
+  validatePassword,
+} from "@/lib/shared/helpers/validatePassword";
 
 const initialValues = {
   validForm: "",
@@ -144,7 +143,7 @@ export function CreateBackupSidebar({
           }}
           validate={(values) => {
             if (
-              !validatePassphrase(values.passphrase, values.repeatedPassphrase)
+              !validatePassword(values.passphrase, values.repeatedPassphrase)
             ) {
               return { validForm: "false" };
             }
@@ -163,17 +162,15 @@ export function CreateBackupSidebar({
                   ))}
                   <PasswordField
                     data-testid={"passphrase"}
-                    label={"Enter a Security Phrase"}
+                    label={"Sicherheitsphrase vergeben"}
                     name={fieldName("passphrase")}
                     visibilityLabel={"visiblePassphrase"}
-                    validate={validateLength(1, 10)}
                   />
                   <PasswordField
                     data-testid={"repeatedPassphrase"}
-                    label={"Confirm your Security Phrase"}
+                    label={"Sicherheitsphrase wiederholen"}
                     name={fieldName("repeatedPassphrase")}
                     visibilityLabel={"visibleRepeatedPassphrase"}
-                    validate={validateLength(1, 10)}
                   />
                 </Stack>
                 <PasswortRequirementHints
@@ -225,7 +222,7 @@ function PasswortRequirementHints({
         Anforderungen an Sicherheitsphrasen:
       </Typography>
 
-      {getPassphraseValidityInfo(password, repeatedPassword).map(
+      {getPasswordValidityInfo(password, repeatedPassword).map(
         ({ message, valid }) => (
           <Typography
             fontWeight={"lighter"}
diff --git a/employee-portal/src/lib/businessModules/chat/components/secureBackup/ResetBackupModal.tsx b/employee-portal/src/lib/businessModules/chat/components/secureBackup/ResetBackupModal.tsx
index c7ffd916faa2ad3da9a7bbe35ad7f8c509832cc7..db80bc8dcf35407c5fd32771959c13c9916de93c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/secureBackup/ResetBackupModal.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/secureBackup/ResetBackupModal.tsx
@@ -29,18 +29,18 @@ export function ResetBackupModal(props: Omit<BaseModalProps, "children">) {
 
   return (
     <BaseModal
-      modalTitle="Reset everything"
+      modalTitle="Alles zurücksetzen"
       key="reset-backup-modal"
       {...props}
     >
       <>
         <Typography>
-          Only do this if you have no other device to complete verification
-          with.
+          Tun Sie dies nur, wenn Sie kein anderes Gerät haben, mit dem Sie die
+          Verifizierung abschließen können.
         </Typography>
         <Typography textColor="text.secondary">
-          If you reset everything, you will restart with no trusted sessions, no
-          trusted users, and might not be able to see past messages.
+          Wenn Sie alles zurücksetzen, können Sie möglicherweise frühere
+          Nachrichten nicht mehr lesen.
         </Typography>
         <Stack
           direction="row"
@@ -54,7 +54,7 @@ export function ResetBackupModal(props: Omit<BaseModalProps, "children">) {
             onClick={props.onClose}
             data-testid="confirmationDialogCancel"
           >
-            Cancel
+            Abbrechen
           </Button>
           <Button
             size="sm"
@@ -63,7 +63,7 @@ export function ResetBackupModal(props: Omit<BaseModalProps, "children">) {
             onClick={handleResetAllClick}
             data-testid="confirmationDialogConfirm"
           >
-            Reset everything
+            Alles zurücksetzen
           </Button>
         </Stack>
       </>
diff --git a/employee-portal/src/lib/businessModules/chat/components/secureBackup/RestoreBackupSidebar.tsx b/employee-portal/src/lib/businessModules/chat/components/secureBackup/RestoreBackupSidebar.tsx
index 535ed7f05e998599d0f9d70023969166b8f214ed..5cc59af8cf7140f28c41428dba8949aaa0d75731 100644
--- a/employee-portal/src/lib/businessModules/chat/components/secureBackup/RestoreBackupSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/secureBackup/RestoreBackupSidebar.tsx
@@ -65,7 +65,8 @@ export function RestoreBackupSidebar({
       return undefined;
     } catch {
       return {
-        passphrase: "Error while verifying the device. Is the phrase correct?",
+        passphrase:
+          "Fehler bei der Verifizierung des Geräts. Ist die Phrase korrekt?",
       };
     }
   }
@@ -86,10 +87,10 @@ export function RestoreBackupSidebar({
         values.passphrase,
       );
       setClientState(ClientState.Prepared);
-      snackbar.confirmation("Your device is now verified");
+      snackbar.confirmation("Ihr Gerät wurde nun verifiziert");
     } catch (e) {
       handleClose();
-      snackbar.error("Cannot access chat");
+      snackbar.error("Kein Zugriff auf den Chat");
       setClientState(ClientState.Error);
       logger.error(e);
     }
@@ -118,23 +119,23 @@ export function RestoreBackupSidebar({
                   ))}
                   <PasswordField
                     data-testid={"passphrase"}
-                    label={"Enter a Security Phrase"}
+                    label={"Sicherheitsphrase vergeben"}
                     name={fieldName("passphrase")}
                     visibilityLabel={"visiblePassphrase"}
                   />
                   <Stack direction="row" spacing={0.5}>
                     <Typography level="body-sm" color="neutral">
-                      Forgotten or lost recovery phrase?
+                      Wiederherstellungsphrase vergessen oder verloren?{` `}
+                      <Link
+                        component="button"
+                        type="button"
+                        level="body-sm"
+                        color="danger"
+                        onClick={() => setModalOpen(true)}
+                      >
+                        Alles zurücksetzen
+                      </Link>
                     </Typography>
-                    <Link
-                      component="button"
-                      type="button"
-                      level="body-sm"
-                      color="danger"
-                      onClick={() => setModalOpen(true)}
-                    >
-                      Reset all
-                    </Link>
                   </Stack>
                 </Stack>
               </SidebarContent>
diff --git a/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx b/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
index 33332e5ba2667e5346a48cf17dd5e0d19f658123..4e6600ba47112c3d13d0d9a4a0e0550caef804b6 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
@@ -152,7 +152,6 @@ export function ChatClientProvider({ children }: Readonly<RequiresChildren>) {
       ) {
         showMessageTeaser({
           username: room.name,
-          avatar: undefined,
           text:
             guestCount > 1
               ? `${sender.displayName}: ${messageContent.body}`
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..51dc0a95ec723a65c3465524316820e01ec3c08b
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useContext } from "react";
+
+import { ChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
+import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+
+export function useMatrixClient() {
+  const { canAccessChat, userSettings } = useChat();
+  const chatContext = useContext(ChatClientContext);
+
+  const isChatEnabled =
+    canAccessChat && userSettings.chatUsageEnabled && chatContext?.matrixClient;
+
+  return isChatEnabled ? chatContext.matrixClient : null;
+}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
index 207169aece0c567dca0488729ba97f92748b4ad9..ba6639c1c6aad40d4b8b090122efd286c3755d86 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
@@ -18,6 +18,7 @@ import { ClientState } from "@/lib/businessModules/chat/shared/enums";
 import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import {
   Message,
+  ReadConfirmationsPerUser,
   RoomEventDetails,
   isChatMessageType,
   isMessageTypeWithBody,
@@ -30,6 +31,7 @@ export function useRoomMessages() {
   const { matrixClient, clientState } = useChatClientContext();
   const messagesLimit = 10;
   const [isLoading, setIsLoading] = useState(false);
+  const loggedInUserId = matrixClient.getUserId();
 
   async function handleSendMessage(text: string, mentionedUsers?: string[]) {
     try {
@@ -169,7 +171,23 @@ export function useRoomMessages() {
             ) {
               return;
             }
-            return await onMessage({ event, room, removed: false });
+            const readReceipts = room.getReceiptsForEvent(event);
+            const message = await onMessage({ event, room, removed: false });
+            const readReceiptsObj =
+              readReceipts?.reduce<ReadConfirmationsPerUser>(
+                (acc, { userId, data }) => {
+                  if (userId === loggedInUserId) return acc;
+                  return {
+                    ...acc,
+                    [userId]: {
+                      timestamp: data.ts,
+                      eventId: event.event.event_id ?? "",
+                    },
+                  };
+                },
+                {},
+              );
+            return { ...message, readReceipts: readReceiptsObj };
           }),
         );
 
@@ -203,7 +221,7 @@ export function useRoomMessages() {
         setIsLoading(false);
       }
     },
-    [matrixClient, onMessage],
+    [loggedInUserId, matrixClient, onMessage],
   );
 
   async function paginateMessages() {
diff --git a/employee-portal/src/lib/businessModules/chat/shared/types.ts b/employee-portal/src/lib/businessModules/chat/shared/types.ts
index a378b3b4208400239594bde9bf27e5530bcec789..cdf1c37f2aef4826bb644e5c0a533cb853fc092c 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/types.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/types.ts
@@ -29,6 +29,7 @@ export interface Message {
   sender: User | null;
   roomId: string;
   mentions?: string[];
+  readReceipts?: ReadConfirmationsPerUser;
 }
 
 export function isChatMessageType(data: unknown): data is Message {
diff --git a/employee-portal/src/lib/businessModules/chat/shared/utils.ts b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
index 4fc7dc5d3ae62ce01c2cd59907539cc5ad2f08ea..7207406c5a54d36e58758abd98160f3fd695d474 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/utils.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
@@ -29,6 +29,7 @@ import { isEmpty, isStrictEqual, isString, last } from "remeda";
 
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
 import {
+  Message,
   Presence,
   ReadConfirmationsPerUser,
   RoomLastMessage,
@@ -98,16 +99,6 @@ export function getDirectMessageMember(room: RoomWithCommunicationType) {
   return room.room.getAvatarFallbackMember();
 }
 
-export function getDMMemberInfo(
-  room: Room,
-  communicationType: CommunicationType,
-) {
-  if (communicationType === CommunicationType.PublicRoom) {
-    return;
-  }
-  return room.getAvatarFallbackMember();
-}
-
 export function formatUserReceipts(
   userReceipts: ReadConfirmationsPerUser | undefined,
 ): Record<string, string[]> | undefined {
@@ -315,14 +306,14 @@ export async function getRoomLastMessage(
   }
 }
 
-export function convertMessageTimestamp(timestamp?: Date | null) {
+export function formatChatDate(timestamp?: Date | null) {
   if (!timestamp) return "";
 
   if (isToday(timestamp)) {
-    return format(timestamp, "hh:mm");
+    return format(timestamp, "HH:mm");
   }
 
-  return format(timestamp, "MM/dd");
+  return formatDateForChat(timestamp);
 }
 
 export function getDayLabel(date: Date): string {
@@ -438,3 +429,15 @@ export async function leaveRoom(matrixClient: MatrixClient, roomId?: string) {
     await matrixClient.leave(roomId);
   } catch {}
 }
+
+export function allMessagesRead(
+  matrixClient: MatrixClient,
+  newMessages: Message[],
+) {
+  newMessages.forEach((message) => {
+    void markAllMessagesAsRead({
+      roomId: message.roomId,
+      matrixClient: matrixClient,
+    });
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts
index 75e5e44c2c23abb0ec2c9646671e17aa0a20a683..30e3cd4ba41327f4e9b4767105a5995d5b3c85ec 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts
@@ -35,6 +35,7 @@ export interface FormChecklistDefinitionVersion {
 
 export function useCreateChecklistDefinition() {
   const checklistDefinitionApi = useChecklistDefinitionApi();
+  const snackbar = useSnackbar();
   return useHandledMutation({
     mutationFn: async (cldVersion: FormChecklistDefinitionVersion) => {
       const createdChecklist: ApiCreateNewChecklistDefinitionRequest = {
@@ -52,11 +53,15 @@ export function useCreateChecklistDefinition() {
         createdChecklist,
       );
     },
+    onSuccess: () => {
+      snackbar.confirmation("Checkliste erfolgreich erstellt.");
+    },
   });
 }
 
 export function useAddChecklistDefinitionVersion() {
   const checklistDefinitionApi = useChecklistDefinitionApi();
+  const snackbar = useSnackbar();
   return useHandledMutation({
     mutationFn: async ({
       defId,
@@ -81,11 +86,15 @@ export function useAddChecklistDefinitionVersion() {
         createdChecklist,
       );
     },
+    onSuccess: () => {
+      snackbar.confirmation("Checkliste erfolgreich aktualisiert");
+    },
   });
 }
 
 export function useEditDraftChecklistDefinitionVersion() {
   const checklistDefinitionApi = useChecklistDefinitionApi();
+  const snackbar = useSnackbar();
   return useHandledMutation({
     mutationFn: async ({
       versionId,
@@ -110,6 +119,9 @@ export function useEditDraftChecklistDefinitionVersion() {
         request,
       );
     },
+    onSuccess: () => {
+      snackbar.confirmation("Checkliste erfolgreich aktualisiert");
+    },
   });
 }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
index d065af99b16fda0de8372866d246d8d8a2f2c50c..c1405be42f1c83e5c9d72111012a452f022644a1 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection";
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { Stack } from "@mui/joy";
@@ -20,8 +21,13 @@ import {
   useEditDraftChecklistDefinitionVersion,
 } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
 import { ChecklistDefinitionHeaderCard } from "@/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderCard";
-import { ChecklistDefinitionHeaderRow } from "@/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow";
+import {
+  ChecklistDefinitionHeaderRow,
+  ChecklistDefinitionSubmitButtons,
+} from "@/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { useHasUserRolesCheck } from "@/lib/shared/hooks/useAccessControl";
 
 import { ChecklistDefinitionSectionsList } from "./sections/ChecklistDefinitionSectionsList";
 
@@ -42,6 +48,10 @@ export function EditChecklistDefinition({
   const { mutateAsync: addCldVersion } = useAddChecklistDefinitionVersion();
   const { mutateAsync: editDraftCldVersion } =
     useEditDraftChecklistDefinitionVersion();
+  const [canEditChecklists, canEditCoreChecklists] = useHasUserRolesCheck([
+    ApiUserRole.InspectionChecklistdefinitionsWrite,
+    ApiUserRole.InspectionCorechecklistdefinitionsEdit,
+  ]);
 
   const hasDraft = cldVersion?.hasDraft ?? false;
   const isNewestVersion =
@@ -49,6 +59,9 @@ export function EditChecklistDefinition({
     (cldVersion?.context.validTo === undefined &&
       cldVersion?.context.published === true);
   const readOnlyMode = readonly ?? !isNewestVersion;
+  const canSeeSaveActions =
+    canEditChecklists &&
+    (canEditCoreChecklists || !cldVersion?.isCoreChecklist);
 
   const formData: FormChecklistDefinitionVersion = useMemo(
     () =>
@@ -136,6 +149,16 @@ export function EditChecklistDefinition({
               version={cldVersion?.context.version}
             />
             <ChecklistDefinitionSectionsList readOnlyMode={readOnlyMode} />
+            {canSeeSaveActions && !readOnlyMode && (
+              <ButtonBar
+                right={
+                  <ChecklistDefinitionSubmitButtons
+                    isSubmitting={isSubmitting}
+                    onPublish={(shouldPublish) => (publish = shouldPublish)}
+                  />
+                }
+              />
+            )}
           </Stack>
         </FormPlus>
       )}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow.tsx
index f0f7bb112b725788088f41118b1396dcf1276622..5e4ba235dccb23cb15e6e784f9cb385d49f98697 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow.tsx
@@ -88,23 +88,39 @@ export function ChecklistDefinitionHeaderRow({
             alignItems="center"
             justifyContent={"space-between"}
           >
-            <SubmitButton
-              key="draft"
-              variant="outlined"
-              submitting={isSubmitting}
-              onClick={() => onPublish(false)}
-            >
-              Als Entwurf speichern
-            </SubmitButton>
-            <SubmitButton
-              key="publish"
-              submitting={isSubmitting}
-              onClick={() => onPublish(true)}
-            >
-              Checkliste veröffentlichen
-            </SubmitButton>
+            <ChecklistDefinitionSubmitButtons
+              isSubmitting={isSubmitting}
+              onPublish={onPublish}
+            />
           </Stack>
         ))}
     </Stack>
   );
 }
+
+export function ChecklistDefinitionSubmitButtons({
+  isSubmitting = false,
+  onPublish,
+}: Readonly<
+  Pick<ChecklistDefinitionHeaderRowProps, "isSubmitting" | "onPublish">
+>) {
+  return (
+    <>
+      <SubmitButton
+        key="draft"
+        variant="outlined"
+        submitting={isSubmitting}
+        onClick={() => onPublish(false)}
+      >
+        Als Entwurf speichern
+      </SubmitButton>
+      <SubmitButton
+        key="publish"
+        submitting={isSubmitting}
+        onClick={() => onPublish(true)}
+      >
+        Checkliste veröffentlichen
+      </SubmitButton>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/models/Anamnesis.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Anamnesis.ts
index bf08c6f239033eb151836bc61868ebb45e90be35..ab9b0cc9aaa03e66e7dfce9e9462e7a60078cf66 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/models/Anamnesis.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Anamnesis.ts
@@ -53,5 +53,6 @@ export function mapAnamnesis(response: ApiAnamnesis): Anamnesis {
     promotionTherapyAndAidInfo: response.promotionTherapyAndAidInfo,
     interestsAndSportsInfo: response.interestsAndSportsInfo,
     migrationBackground: response.migrationBackground,
+    personalConspicuities: response.personalConspicuities,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/queries/configApi.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/queries/configApi.ts
index e8a9bd01f19a18f12c5d764f65875acc2d185f42..4b529e0ae8974898a1d8300bc3e86a3a9a4619a7 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/queries/configApi.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/queries/configApi.ts
@@ -19,6 +19,26 @@ export function useGetLocationSelectionMode() {
   return locationSelectionMode;
 }
 
+export function useIsDirectProcedureTypeAssignmentOnImport() {
+  const configApi = useConfigApi();
+  const { data: isDirectProcedureTypeAssignmentOnImport } = useSuspenseQuery(
+    getIsDirectProcedureTypeAssignmentOnImportQuery(configApi),
+  );
+  return isDirectProcedureTypeAssignmentOnImport;
+}
+
+export function getIsDirectProcedureTypeAssignmentOnImportQuery(
+  configApi: SchoolEntryConfigApi,
+) {
+  return queryOptions({
+    queryKey: configApiQueryKey(["getConfig"]),
+    queryFn: () => configApi.getConfig(),
+    select: (response) => response.isDirectProcedureTypeAssignmentOnImport,
+    staleTime: CACHE_DURATION_1DAY,
+    gcTime: CACHE_DURATION_1DAY,
+  });
+}
+
 export function getLocationSelectionModeQuery(configApi: SchoolEntryConfigApi) {
   return queryOptions({
     queryKey: configApiQueryKey(["getConfig"]),
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/LabelChip.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/LabelChip.tsx
index 379aa63591afdaf5f59a41f83d08458d55a10e88..718ff4b0c8d3661d73033a29234cb5a835da03aa 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/LabelChip.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/LabelChip.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Chip } from "@mui/joy";
+import { Chip, Tooltip } from "@mui/joy";
 
 import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
 
@@ -36,14 +36,17 @@ function c(color: number) {
 
 export function LabelChip(props: Props) {
   return (
-    <Chip
-      variant="solid"
-      sx={{
-        backgroundColor: props.label.hexColor,
-        color: contrastColor(props.label.hexColor),
-      }}
-    >
-      {props.label.name}
-    </Chip>
+    <Tooltip title={props.label.name} size="sm" placement="right">
+      <Chip
+        variant="solid"
+        sx={{
+          backgroundColor: props.label.hexColor,
+          color: contrastColor(props.label.hexColor),
+          maxWidth: "100%",
+        }}
+      >
+        {props.label.name}
+      </Chip>
+    </Tooltip>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/InterestsAndSportInfoForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/InterestsAndSportInfoForm.tsx
index 5ab62f85fdae8446ceb0f0ba1d9938df870ed9bf..1b200ad9905cac40199ca68a20877233a0eb48a2 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/InterestsAndSportInfoForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/InterestsAndSportInfoForm.tsx
@@ -36,12 +36,11 @@ export function InterestAndSportsInfoForm() {
           sx={BOOLEAN_SELECT_STYLE}
           allowDeselection
         />
-        <BooleanSelectField
+        <InputField
           name={interestAndSportsInfo("clubSport")}
           label="Sport im Verein"
+          type="text"
           component={HorizontalField}
-          sx={BOOLEAN_SELECT_STYLE}
-          allowDeselection
         />
         <InputField
           name={interestAndSportsInfo("otherInterests")}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
index 8a18aafb39c23cf3cdf082125636dc65de9d447b..41a24d463d2281763795be144fd248dc429a5619 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
@@ -27,6 +27,7 @@ interface ImportDataFieldsProps {
   listType: ImportListType;
   requireSchoolYear: boolean;
   locationSelectionMode: ApiLocationSelectionMode;
+  isDirectProcedureTypeAssignmentOnImport: boolean;
   setFieldValue: SetFieldValueHelper;
 }
 
@@ -67,14 +68,16 @@ export function ImportDataFields(props: ImportDataFieldsProps) {
 
   return (
     <Stack gap={4}>
-      <RadioGroupField
-        name="listType"
-        orientation="horizontal"
-        onChange={handleChangeListType}
-      >
-        <Radio value={ImportListType.SchoolList} label="Schulliste" />
-        <Radio value={ImportListType.CitizenList} label="Bürgeramtsliste" />
-      </RadioGroupField>
+      {!props.isDirectProcedureTypeAssignmentOnImport && (
+        <RadioGroupField
+          name="listType"
+          orientation="horizontal"
+          onChange={handleChangeListType}
+        >
+          <Radio value={ImportListType.SchoolList} label="Schulliste" />
+          <Radio value={ImportListType.CitizenList} label="Bürgeramtsliste" />
+        </RadioGroupField>
+      )}
       {props.listType === ImportListType.SchoolList && (
         <Stack gap={1}>
           <SearchContactField
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
index 6d4c7f70f62380f63624433b73fd59f0f149d60f..0897b3b6b4e5f23a11a000d4284c6c8da7b7e78f 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
@@ -11,7 +11,10 @@ import { Formik } from "formik";
 import { useRouter } from "next/navigation";
 
 import { useImportData } from "@/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi";
-import { useGetLocationSelectionMode } from "@/lib/businessModules/schoolEntry/api/queries/configApi";
+import {
+  useGetLocationSelectionMode,
+  useIsDirectProcedureTypeAssignmentOnImport,
+} from "@/lib/businessModules/schoolEntry/api/queries/configApi";
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { ImportListType } from "@/lib/businessModules/schoolEntry/features/procedures/importData/importTypes";
 import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
@@ -46,6 +49,8 @@ export function ImportDataSidebar() {
     ApiSchoolEntryFeature.SchoolYear,
   );
   const locationSelectionMode = useGetLocationSelectionMode();
+  const isDirectProcedureTypeAssignmentOnImport =
+    useIsDirectProcedureTypeAssignmentOnImport();
   const importData = useImportData(isSchoolYearEnabled);
 
   async function handleSubmit(values: ImportDataValues) {
@@ -69,12 +74,18 @@ export function ImportDataSidebar() {
                 <ImportResult
                   file={importData.data.file}
                   statistics={importData.data.statistics}
+                  isDirectProcedureTypeAssignmentOnImport={
+                    isDirectProcedureTypeAssignmentOnImport
+                  }
                 />
               ) : (
                 <ImportDataFields
                   listType={values.listType}
                   requireSchoolYear={isSchoolYearEnabled}
                   locationSelectionMode={locationSelectionMode}
+                  isDirectProcedureTypeAssignmentOnImport={
+                    isDirectProcedureTypeAssignmentOnImport
+                  }
                   setFieldValue={setFieldValue}
                 />
               )}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
index 806920cef6c58052cbe4333892d49698fbe71538..a884d404f002d036c7f1d7c61fb4f4bcb9e74562 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
@@ -67,6 +67,7 @@ function getStatusHeading(
 interface ImportResultProps {
   file: File;
   statistics: ApiImportStatistics;
+  isDirectProcedureTypeAssignmentOnImport: boolean;
 }
 
 export function ImportResult(props: ImportResultProps) {
@@ -83,7 +84,12 @@ export function ImportResult(props: ImportResultProps) {
             {getStatusHeading(props.statistics, isMergeEnabled)}
           </Typography>
           {isMergeEnabled && (
-            <ImportResultProceduresSummary result={props.statistics} />
+            <ImportResultProceduresSummary
+              result={props.statistics}
+              isDirectProcedureTypeAssignmentOnImport={
+                props.isDirectProcedureTypeAssignmentOnImport
+              }
+            />
           )}
         </Stack>
         {props.statistics.total > 0 && (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResultProceduresSummary.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResultProceduresSummary.tsx
index 6a04e6a6665ec8218e3f725e7bf73c58979346a6..c612452dd8c873b49a6a2f01eacb1e2e712e6d76 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResultProceduresSummary.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResultProceduresSummary.tsx
@@ -13,6 +13,7 @@ interface ImportResultProcedures {
 
 interface ImportResultProceduresSummaryProps {
   result: ImportResultProcedures;
+  isDirectProcedureTypeAssignmentOnImport: boolean;
 }
 
 interface SummaryItemProps {
@@ -43,6 +44,13 @@ function SummaryItem(props: SummaryItemProps) {
 export function ImportResultProceduresSummary(
   props: ImportResultProceduresSummaryProps,
 ) {
+  const createdOrMerged = props.isDirectProcedureTypeAssignmentOnImport
+    ? "angelegt"
+    : "angelegt oder zusammengeführt";
+  const mergeFailedMessage = props.isDirectProcedureTypeAssignmentOnImport
+    ? "nicht angelegt wegen Duplikaten im Bestand"
+    : "konnten nicht zusammengeführt werden";
+
   return (
     <Stack gap={1}>
       {props.result.created > 0 && (
@@ -58,14 +66,11 @@ export function ImportResultProceduresSummary(
         />
       )}
       {props.result.created == 0 && props.result.merged == 0 && (
-        <SummaryItem
-          content="0 angelegt oder zusammengeführt"
-          color="primary"
-        />
+        <SummaryItem content={`0 ${createdOrMerged}`} color="primary" />
       )}
       {props.result.mergeFailed > 0 && (
         <SummaryItem
-          content={`${props.result.mergeFailed} konnten nicht zusammengeführt werden`}
+          content={`${props.result.mergeFailed} ${mergeFailedMessage}`}
           color="danger"
         />
       )}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2a246e519474ae727f9a8309ab90403e1dea603f
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete.tsx
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Autocomplete, AutocompleteOption } from "@mui/joy";
+
+import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
+import { useGetLabels } from "@/lib/businessModules/schoolEntry/api/queries/labelApi";
+import { LabelChip } from "@/lib/businessModules/schoolEntry/features/labels/LabelChip";
+
+interface LabelAutocompleteProps {
+  name: string;
+  value: Label[];
+  onChange: (newValue: Label[]) => void;
+}
+
+export function LabelAutocomplete(props: LabelAutocompleteProps) {
+  const labelsQuery = useGetLabels();
+
+  return (
+    <Autocomplete
+      name={props.name}
+      multiple
+      placeholder="Kennung"
+      options={labelsQuery.data}
+      getOptionLabel={(option) => option.name}
+      isOptionEqualToValue={(option, value) => option.id === value.id}
+      value={props.value}
+      onChange={(_, newValue) => {
+        props.onChange(newValue);
+      }}
+      renderOption={(props, label) => (
+        <AutocompleteOption {...props} key={label.id}>
+          <LabelChip label={label} />
+        </AutocompleteOption>
+      )}
+      renderTags={(tags, props) =>
+        tags.map((label, index) => (
+          <LabelChip {...props({ index })} key={label.id} label={label} />
+        ))
+      }
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
index f2fcf07ce71219e85c6771751bbb265dcd122303..3b616a00441a39df795716e584dc29d81a73f306 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
@@ -7,44 +7,26 @@ import {
   BaseField,
   useBaseField,
 } from "@eshg/lib-portal/components/formFields/BaseField";
-import { Autocomplete, AutocompleteOption } from "@mui/joy";
 
 import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
-import { useGetLabels } from "@/lib/businessModules/schoolEntry/api/queries/labelApi";
-import { LabelChip } from "@/lib/businessModules/schoolEntry/features/labels/LabelChip";
+import { LabelAutocomplete } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete";
 
 interface LabelSelectionProps {
   onChange: (newValue: Label[]) => void;
 }
 
 export function LabelSelection(props: LabelSelectionProps) {
-  const labelsQuery = useGetLabels();
   const field = useBaseField<Label[]>({ name: "labels" });
 
   return (
     <BaseField label="Kennungen">
-      <Autocomplete
+      <LabelAutocomplete
         name={field.input.name}
-        multiple
-        placeholder="Kennung"
-        options={labelsQuery.data}
-        getOptionLabel={(option) => option.name}
-        isOptionEqualToValue={(option, value) => option.id === value.id}
         value={field.input.value}
-        onChange={(_, newValue) => {
+        onChange={(newValue) => {
           void field.helpers.setValue(newValue);
           props.onChange(newValue);
         }}
-        renderOption={(props, label) => (
-          <AutocompleteOption {...props} key={label.id}>
-            <LabelChip label={label} />
-          </AutocompleteOption>
-        )}
-        renderTags={(tags, props) =>
-          tags.map((label, index) => (
-            <LabelChip {...props({ index })} key={label.id} label={label} />
-          ))
-        }
       />
     </BaseField>
   );
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
index b9a9b924b1e4f35feecdc58d5207a424bf55d320..8b7348bb9c34c47ccc56ad5a9342d93aaccfcfec 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
@@ -14,10 +14,12 @@ import {
   toUtcDate,
 } from "@eshg/lib-portal/helpers/dateTime";
 import { FormControl, FormLabel, Input, Select } from "@mui/joy";
-import { isDefined } from "remeda";
+import { isDefined, isEmpty } from "remeda";
 
+import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { PROCEDURE_TYPE_OPTIONS } from "@/lib/businessModules/schoolEntry/features/procedures/options";
+import { LabelAutocomplete } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete";
 import { SearchSchoolFilter } from "@/lib/businessModules/schoolEntry/features/procedures/proceduresTable/SearchSchoolFilter";
 import { SchoolYearAutocomplete } from "@/lib/businessModules/schoolEntry/features/procedures/shared/schoolYear";
 import { ResetButton } from "@/lib/shared/components/ResetButton";
@@ -36,8 +38,7 @@ export type ProcedureFilters = Pick<
   | "dayOfAppointmentFilter"
   | "hasAppointmentFilter"
   | "schoolYearFilter"
-  | "labelsFilter"
->;
+> & { labelsFilter?: Label[] };
 
 const FILTER_NAMES: Record<keyof ProcedureFilters, string> = {
   procedureTypeFilter: "Art",
@@ -189,6 +190,19 @@ export function ProcedureFilterSettings(props: ProcedureFilterSettingsProps) {
             <SelectOptions options={PROCEDURE_TYPE_OPTIONS} />
           </Select>
         </FormControl>
+        <FormControl>
+          <FormLabel>Kennungen</FormLabel>
+          <LabelAutocomplete
+            name="labels"
+            value={props.filterFormValues.labelsFilter ?? []}
+            onChange={(newValue) => {
+              props.setFilterFormValue(
+                "labelsFilter",
+                isEmpty(newValue) ? undefined : newValue,
+              );
+            }}
+          />
+        </FormControl>
       </FilterSettingsContent>
     </FilterSettingsSheet>
   );
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
index 6f5f848f65f57be89a3d05ab3605b63321c998d3..7b6fa26486695a2ea5eccdf046c9339e8f50899a 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
@@ -106,6 +106,7 @@ export function ProceduresTable(props: ProceduresTableProps) {
     pageNumber: tableControl.paginationProps.pageNumber,
     pageSize: tableControl.paginationProps.pageSize,
     ...filterValues,
+    labelsFilter: filterValues.labelsFilter?.map((label) => label.id),
     ...personSearch.searchParams,
     sortKey: getSortKey(tableControl.tableSorting),
     sortDirection: getSortDirection(tableControl.tableSorting),
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
index 7743b5df98d866729fb3e953c877aabc57bc1188..730d52caf8b1a263288b1977968a52ab6779b015 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
@@ -20,6 +20,7 @@ export function useUpdateReport(onSuccess: () => void) {
           props.model.description.trim().length > 0
             ? props.model.description.trim()
             : undefined,
+        type: "UpdateNameAndDescriptionReportSeriesRequest",
       }),
     onSuccess: () => {
       snackbar.confirmation("Report bearbeitet");
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
index 8b2ab1e19ac8cfcb3ac6ed872ae52a871c3684ce..2bf909ca9794b491b3ef118a1b2f5c6dc23b1cab 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
@@ -139,7 +139,7 @@ export function mapToStatisticDetailsView(
     start: result.statisticInfo.timeRangeStart,
     end: mapTimeRangeEndApiToFrontend(result.statisticInfo.timeRangeEnd),
     createdAt: result.statisticInfo.createdAt,
-    createdBy: fullName(result.userDto),
+    createdBy: fullName(result.user),
     dataSource: {
       // We only have one datasource currently. If this changes the data structure changes and thus
       // this aggregation method has to become more sophisticated.
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
index 23b44912e05b236ff412f460a6fc4af363055381..929cddef01f959100a9a16662088028d9c8dd8d9 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
@@ -21,7 +21,7 @@ import { mapEvaluations } from "./useGetDetailPageInformation";
 export function mapToReportDetailsView(
   response: ApiGetReportDetailPageResponse,
 ): ReportDetailsView {
-  const user = response.userDto;
+  const user = response.userReportSeries;
   const attributes: FlatAttribute[] = mapTableColumnHeadersToFlatAttributes(
     response.tableColumnHeaders,
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
index 2a18ef3e938368372c134fc157de985e5eb04e01..ca120773b881b473a1c16b251b6d0feeffa7158f 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
@@ -49,7 +49,7 @@ function getActionItems(
   seriesId: string,
   description: string | undefined,
   updateReport: (report: UpdateReportSidebarReportInfo) => void,
-  deleteReportWithConfirmation: (reportId: string, name: string) => void,
+  deleteReportWithConfirmation: (reportId: string) => void,
 ) {
   // Uncomment in https://cronn-gmbh.atlassian.net/browse/ISSUE-5001
   // const rememberReport = {
@@ -81,7 +81,7 @@ function getActionItems(
     },
     {
       label: "Report löschen",
-      onClick: () => deleteReportWithConfirmation(seriesId, name),
+      onClick: () => deleteReportWithConfirmation(seriesId),
       startDecorator: <Delete />,
       color: "danger",
     },
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
index c94093b0250159a4d92513bfac25b6204492200a..ed5c891f140b6adfde16a208d649e878aaf103cc 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
@@ -7,18 +7,17 @@
 
 import { ApiReportType } from "@eshg/employee-portal-api/statistics";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { Box, List, ListItem } from "@mui/joy";
+import { Box } from "@mui/joy";
 import { startTransition, useState } from "react";
 
 import { translateReportType } from "@/lib/businessModules/statistics/api/mapper/translateReportType";
 import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
-import { useDeleteReport } from "@/lib/businessModules/statistics/api/mutations/useDeleteReport";
 import { useGetReportsOverview } from "@/lib/businessModules/statistics/api/queries/useGetReportsOverview";
+import { useDeleteReportWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation";
 import { routes } from "@/lib/businessModules/statistics/shared/routes";
 import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 import { FilterSettings } from "@/lib/shared/components/filterSettings/FilterSettings";
 import { FilterSettingsSheet } from "@/lib/shared/components/filterSettings/FilterSettingsSheet";
 import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition";
@@ -64,8 +63,7 @@ const filterDefinitions: FilterDefinition[] = [
 
 export function ReportsOverview() {
   const snackbar = useSnackbar();
-  const { openConfirmationDialog } = useConfirmationDialog();
-  const deleteReport = useDeleteReport();
+  const deleteReportWithConfirmation = useDeleteReportWithConfirmation();
 
   const { resetPageNumber, page, pageSize, getPaginationProps } =
     usePagination();
@@ -105,27 +103,6 @@ export function ReportsOverview() {
     snackbar.notification("Link in die Zwischenablage kopiert");
   }
 
-  function handleDeleteReport(id: string) {
-    openConfirmationDialog({
-      color: "danger",
-      title: "Report löschen?",
-      description: "Wenn Sie mit dem Löschen fortfahren, wird ...",
-      children: (
-        <List marker="disc">
-          <ListItem>der Report unwiderruflich gelöscht,</ListItem>
-          <ListItem>der Report aus allen Merklisten entfernt,</ListItem>
-          <ListItem>
-            eine Nachricht an die Nutzer:innen gesendet, die den Report in ihrer
-            Merkliste haben.
-          </ListItem>
-        </List>
-      ),
-      cancelLabel: "Abbrechen",
-      confirmLabel: "Ja, löschen",
-      onConfirm: () => deleteReport(id),
-    });
-  }
-
   return (
     <TablePage
       data-testid="statistics-reports-overview-table"
@@ -149,7 +126,7 @@ export function ReportsOverview() {
           wrapHeader
           columns={getReportsOverviewColumns(
             handleClickCopyAddress,
-            handleDeleteReport,
+            deleteReportWithConfirmation,
           )}
           data={reportsOverview.reports}
           noDataComponent={() => (
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.ts b/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx
similarity index 60%
rename from employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.ts
rename to employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx
index 0485d4998799d8b64315a389c7ccc69382baf8c3..b8c27571b8f9c0745a4d5b144a25ef50d9c5bc14 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { List, ListItem } from "@mui/joy";
 import { useRouter } from "next/navigation";
 import { isDefined } from "remeda";
 
@@ -24,12 +25,23 @@ export function useDeleteReportWithConfirmation({
     },
   });
 
-  function deleteReportWithConfirmation(seriesId: string, reportName: string) {
+  function deleteReportWithConfirmation(seriesId: string) {
     openConfirmationDialog({
-      title: "Report löschen?",
-      description: `Der Report "${reportName}" wird dann unwiderruflich gelöscht.`,
-      confirmLabel: "Report löschen",
       color: "danger",
+      title: "Report löschen?",
+      description: "Wenn Sie mit dem Löschen fortfahren, wird ...",
+      children: (
+        <List marker="disc">
+          <ListItem>der Report unwiderruflich gelöscht,</ListItem>
+          <ListItem>der Report aus allen Merklisten entfernt,</ListItem>
+          <ListItem>
+            eine Nachricht an die Nutzer:innen gesendet, die den Report in ihrer
+            Merkliste haben.
+          </ListItem>
+        </List>
+      ),
+      cancelLabel: "Abbrechen",
+      confirmLabel: "Ja, löschen",
       onConfirm: () => {
         deleteReport(seriesId);
       },
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
index db492ecab0ac92a12b422e3be65fc53a9c111067..dc8ca535e79a327a32894c5b8bbe58523f828bc3 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
@@ -62,7 +62,12 @@ export function EvaluationSortOrderSelect(
       }}
       color="primary"
       value={props.sortOrder}
-      onChange={(_, value) => props.onSortOrderChange(value!)}
+      onChange={(_, value) => {
+        if (value === null) {
+          return;
+        }
+        props.onSortOrderChange(value);
+      }}
     >
       <SelectOptions options={buildEnumOptions(evaluationSortOrderOptions)} />
     </Select>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
index e9eace62baf13b26b0edf797c2cd43762f655567..98125424e8928542d718ee3ca799cb3a690fbf40 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
@@ -49,7 +49,7 @@ const meta = {
 };
 
 function columns(
-  deleteReportWithConfirmation: (reportId: string, name: string) => void,
+  deleteReportWithConfirmation: (reportId: string) => void,
   editReport: (report: SingleReport) => void,
 ) {
   return [
@@ -104,10 +104,7 @@ function columns(
             {
               label: "Löschen",
               onClick: () =>
-                deleteReportWithConfirmation(
-                  props.row.original.seriesId,
-                  props.row.original.name,
-                ),
+                deleteReportWithConfirmation(props.row.original.seriesId),
               startDecorator: <Delete />,
               color: "danger",
             },
diff --git a/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx b/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx
index e47ff6f7d3954ed1685a4f6e4fddcfe2ebf9fb80..9ba7533036a022da55beb721366e63ae01e002a2 100644
--- a/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx
+++ b/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx
@@ -29,7 +29,6 @@ import { getStatusColor } from "@/lib/businessModules/chat/shared/utils";
 
 interface SnackbarValues {
   username: string;
-  avatar?: string | null;
   text: string;
   link: string;
   userPresence: string;
@@ -46,13 +45,13 @@ type SnackbarValuesWithoutKey = Omit<SnackbarValues, "key">;
 function BaseSnackbar({ snackbar, onClose }: Readonly<BaseSnackbarProps>) {
   const pathname = usePathname();
   const { tryNavigate } = useNavigation();
-  const { chatSidebar } = useChat();
+  const { chatSidebar, userSettings } = useChat();
 
   useEffect(() => {
-    if (pathname === routes.index) {
+    if (pathname === routes.index || chatSidebar.isOpen) {
       onClose();
     }
-  }, [onClose, pathname]);
+  }, [onClose, pathname, chatSidebar.isOpen]);
 
   return (
     <Snackbar
@@ -60,7 +59,7 @@ function BaseSnackbar({ snackbar, onClose }: Readonly<BaseSnackbarProps>) {
       variant="soft"
       size="md"
       anchorOrigin={{ vertical: "top", horizontal: "right" }}
-      autoHideDuration={4000}
+      autoHideDuration={5000}
       key={snackbar?.key}
       onClose={(_event, reason) => {
         if (reason !== "clickaway") {
@@ -96,7 +95,7 @@ function BaseSnackbar({ snackbar, onClose }: Readonly<BaseSnackbarProps>) {
                   alignItems: "center",
                 }}
               >
-                {snackbar.userPresence && (
+                {userSettings.sharePresence && snackbar.userPresence && (
                   <Box
                     sx={{
                       width: "0.625rem",
@@ -111,7 +110,6 @@ function BaseSnackbar({ snackbar, onClose }: Readonly<BaseSnackbarProps>) {
                 )}
                 <Typography
                   level="title-md"
-                  fontStyle="Poppins"
                   fontWeight={500}
                   sx={{
                     fontWeight: "bold",
diff --git a/employee-portal/src/lib/shared/helpers/validatePassphrase.tsx b/employee-portal/src/lib/shared/helpers/validatePassphrase.tsx
deleted file mode 100644
index 061bc46d056c3f1897578616c80ffa85cfe7f5c6..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/shared/helpers/validatePassphrase.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-const minimalPassphraseLength = 6;
-
-export function validatePassphrase(
-  passphrase: string,
-  repeatedPassphrase: string,
-): boolean {
-  return (
-    validatePassphraseLength(passphrase) &&
-    validatePassphraseUpperCase(passphrase) &&
-    validatePassphraseLowerCase(passphrase) &&
-    validateSameRepeatedPassphrase(passphrase, repeatedPassphrase)
-  );
-}
-
-export interface PassphraseValidityInfo {
-  message: string;
-  valid: boolean;
-}
-
-export function getPassphraseValidityInfo(
-  passphrase: string,
-  repeatedPassphrase: string,
-): PassphraseValidityInfo[] {
-  const isPasswortLengthValid = validatePassphraseLength(passphrase);
-  const hasPassphraseUpperCaseLetter = validatePassphraseUpperCase(passphrase);
-  const hasPassphraseLowerCaseLetter = validatePassphraseLowerCase(passphrase);
-  const isSameRepeatedPassphrase = validateSameRepeatedPassphrase(
-    passphrase,
-    repeatedPassphrase,
-  );
-
-  const result = [
-    {
-      message: `Mindestens ${minimalPassphraseLength} Zeichen lang`,
-      valid: isPasswortLengthValid,
-    },
-  ];
-  result.push({
-    message: "Mindestens ein Großbuchstabe",
-    valid: hasPassphraseUpperCaseLetter,
-  });
-  result.push({
-    message: "Mindestens ein Kleinbuchstabe",
-    valid: hasPassphraseLowerCaseLetter,
-  });
-  result.push({
-    message: "Muss mit der Wiederholung übereinstimmen",
-    valid: isSameRepeatedPassphrase,
-  });
-  return result;
-}
-
-function validatePassphraseLength(passphrase: string): boolean {
-  return passphrase.length >= minimalPassphraseLength;
-}
-
-function validatePassphraseUpperCase(passphrase: string): boolean {
-  return passphrase.toLowerCase() !== passphrase;
-}
-
-function validatePassphraseLowerCase(passphrase: string): boolean {
-  return passphrase.toUpperCase() !== passphrase;
-}
-
-function validateSameRepeatedPassphrase(
-  passphrase: string,
-  repeatedPassphrase: string,
-): boolean {
-  return passphrase === repeatedPassphrase && passphrase.length !== 0;
-}
diff --git a/reverse-proxy/auth_api_request.conf b/reverse-proxy/auth_api_request.conf
index 314eb661bfa570e8dd8a4f942f224dd6d41c0f17..d8438c6d9d0aae316c21f36b7c2f7ac6d4d38464 100644
--- a/reverse-proxy/auth_api_request.conf
+++ b/reverse-proxy/auth_api_request.conf
@@ -2,4 +2,5 @@ include auth_request.conf;
 
 auth_request_set $resolved_authorization $upstream_http_authorization;
 
-proxy_set_header Authorization  $resolved_authorization;
+proxy_set_header Authorization    $resolved_authorization;
+proxy_set_header X-Correlation-ID $request_id;
diff --git a/reverse-proxy/citizen-portal.conf b/reverse-proxy/citizen-portal.conf
index fb70cebd76a43ee0ffba374ba5659e14e1bdf8a2..97fb38583be6c82b1be208ea0737626316b53cd3 100644
--- a/reverse-proxy/citizen-portal.conf
+++ b/reverse-proxy/citizen-portal.conf
@@ -128,6 +128,7 @@ server {
         proxy_set_header Content-Length "";
         proxy_set_header X-Original-URI     $request_uri;
         proxy_set_header X-Original-Method  $request_method;
+        proxy_set_header X-Correlation-ID   $request_id;
 
         proxy_intercept_errors on;
         error_page 302 = @rewrite_302_to_401;
diff --git a/reverse-proxy/employee-portal.conf b/reverse-proxy/employee-portal.conf
index 154852e98a74f93ff951ccda1eb2896fe91d3bbc..96b4f9c11f2e46e3418e4fc0d0fc7d49979ea153 100644
--- a/reverse-proxy/employee-portal.conf
+++ b/reverse-proxy/employee-portal.conf
@@ -137,6 +137,7 @@ server {
         proxy_set_header Content-Length "";
         proxy_set_header X-Original-URI     $request_uri;
         proxy_set_header X-Original-Method  $request_method;
+        proxy_set_header X-Correlation-ID   $request_id;
 
         proxy_intercept_errors on;
         error_page 302 = @rewrite_302_to_401;