diff --git a/bescheid-manager/pom.xml b/bescheid-manager/pom.xml
index 5f5e43c0d1204185e1f1473897ea2e7d76babb0e..07ca67426a2042ed3f702a4c3ef65e621c984e13 100644
--- a/bescheid-manager/pom.xml
+++ b/bescheid-manager/pom.xml
@@ -15,7 +15,7 @@
 	<version>1.10.0-SNAPSHOT</version>
 
 	<properties>
-		<vorgang-manager.version>2.5.0</vorgang-manager.version>
+		<vorgang-manager.version>2.6.0-SNAPSHOT</vorgang-manager.version>
 		<api-lib.version>0.7.0-SNAPSHOT</api-lib.version>
 	</properties>
 
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/Bescheid.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/Bescheid.java
index e4d7d675f1a11f287f7610aae8682357336bd907..c08d2a90c97761f8da7e20ee63bdbeec6fe20569 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/Bescheid.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/Bescheid.java
@@ -3,17 +3,29 @@ package de.ozgcloud.bescheid;
 import java.io.File;
 import java.util.Optional;
 
+import org.apache.commons.lang3.StringUtils;
+
 import de.ozgcloud.bescheid.vorgang.Vorgang;
 import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.common.binaryfile.FileId;
 import lombok.Builder;
 import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 import lombok.With;
 
 @Builder(toBuilder = true)
 @Getter
 public class Bescheid {
 
+	public static final String FIELD_STATUS = "status";
+	public static final String FIELD_BESCHIEDEN_AM = "beschiedenAm";
+	public static final String FIELD_BEWILLIGT = "bewilligt";
+	public static final String FIELD_BESCHEID_DOCUMENT = "bescheidDocument";
+	public static final String FIELD_ATTACHMENTS = "attachments";
+	public static final String FIELD_SEND_BY = "sendBy";
+	public static final String FIELD_NACHRICHT_TEXT = "nachrichtText";
+	public static final String FIELD_NACHRICHT_SUBJECT = "nachrichtSubject";
+
 	private VorgangId vorgangId;
 
 	private boolean genehmigt;
@@ -26,8 +38,38 @@ public class Bescheid {
 	private String contentType;
 	private long size;
 
+	@Builder.Default
+	private Optional<String> nachrichtSubject = Optional.empty();
 	@Builder.Default
 	private Optional<String> nachrichtText = Optional.empty();
 
 	private Vorgang.ServiceKonto serviceKonto;
+
+	public enum Status {
+		DRAFT, BESCHEID, SEND;
+
+		public boolean not(String value) {
+			return !hasValue(value);
+		}
+
+		public boolean hasValue(String value) {
+			return this.name().equalsIgnoreCase(value);
+		}
+	}
+
+	@RequiredArgsConstructor
+	public enum SendBy {
+		NACHRICHT("NACHRICHT"), MANUAL("MANUAL");
+
+		private final String value;
+
+		public boolean notValue(Object sendByValue) {
+			return !hasValue(sendByValue);
+		}
+
+		public boolean hasValue(Object sendByValue) {
+			return StringUtils.equalsIgnoreCase(value, String.valueOf(sendByValue));
+		}
+
+	}
 }
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java
index b663692ecd5ab4364b432572e57d4faca1ba2511..5a16db15e398136d8f61e4091532f8a0d0b717b3 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java
@@ -25,6 +25,7 @@ package de.ozgcloud.bescheid;
 
 import java.time.LocalDate;
 import java.util.Optional;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 import org.apache.commons.lang3.StringUtils;
@@ -42,6 +43,8 @@ import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandCreatedEvent;
 import de.ozgcloud.command.CommandFailedEvent;
+import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.document.DocumentService;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
@@ -54,30 +57,41 @@ class BescheidEventListener {
 	public static final String CREATE_BESCHEID_ORDER = "CREATE_BESCHEID";
 	public static final String DELETE_BESCHEID_ORDER = "DELETE_BESCHEID";
 	public static final String UPDATE_BESCHEID_ORDER = "UPDATE_BESCHEID";
+	public static final String CREATE_BESCHEID_DOCUMENT_ORDER = "CREATE_BESCHEID_DOCUMENT";
+	public static final String SEND_BESCHEID_ORDER = "SEND_BESCHEID";
+	public static final String SEND_POSTFACH_MAIL_ORDER = "SEND_POSTFACH_MAIL";
 
-	public static final Predicate<Command> IS_CREATE_BESCHEID_COMMAND = command -> command.getOrder().equals(CREATE_BESCHEID_ORDER);
+	public static final Predicate<Command> IS_CREATE_BESCHEID_COMMAND = command -> CREATE_BESCHEID_ORDER.equals(command.getOrder());
 	private static final String IS_CREATE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_CREATE_BESCHEID_COMMAND.test(event.getSource())}";
 
-	public static final Predicate<Command> IS_DELETE_BESCHEID_COMMAND = command -> command.getOrder().equals(DELETE_BESCHEID_ORDER);
+	public static final Predicate<Command> IS_DELETE_BESCHEID_COMMAND = command -> DELETE_BESCHEID_ORDER.equals(command.getOrder());
 	private static final String IS_DELETE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_DELETE_BESCHEID_COMMAND.test(event.getSource())}";
 
-	public static final Predicate<Command> IS_UPDATE_BESCHEID_COMMAND = command -> command.getOrder().equals(UPDATE_BESCHEID_ORDER);
+	public static final Predicate<Command> IS_UPDATE_BESCHEID_COMMAND = command -> UPDATE_BESCHEID_ORDER.equals(command.getOrder());
 	private static final String IS_UPDATE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_UPDATE_BESCHEID_COMMAND.test(event.getSource())}";
 
+	public static final Predicate<Command> IS_CREATE_BESCHEID_DOCUMENT_COMMAND = command -> CREATE_BESCHEID_DOCUMENT_ORDER.equals(command.getOrder());
+	private static final String IS_CREATE_BESCHEID_DOCUMENT = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_CREATE_BESCHEID_DOCUMENT_COMMAND.test(event.getSource())}";
+
+	public static final Predicate<Command> IS_SEND_BESCHEID_COMMAND = command -> SEND_BESCHEID_ORDER.equals(command.getOrder());
+	private static final String IS_SEND_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_BESCHEID_COMMAND.test(event.getSource())}";
+
+	public static final Predicate<Command> IS_SEND_POSTFACH_MAIL_COMMAND = command -> SEND_POSTFACH_MAIL_ORDER.equals(command.getOrder());
+	private static final String IS_SEND_POSTFACH_MAIL = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_POSTFACH_MAIL_COMMAND.test(event.getSource())}";
+
 	private static final String TEMPLATE_GROUP_KIEL = "Kiel";
 	static final String VORGANG_ID_BODYKEY = "vorgangId";
 	static final String BESCHEID_VOM_BODYKEY = "bescheidVom";
 	static final String GENEHMIGT_BODYKEY = "genehmigt";
 
 	private static final String LOG_MESSAGE_TEMPLATE = "{}. Command failed.";
-	private static final String CREATE_BESCHEID_ERROR_MESSAGE = "Error on executing Create Bescheid Command.";
-	private static final String DELETE_BESCHEID_ERROR_MESSAGE = "Error on executing Delete Bescheid Command.";
-	private static final String UPDATE_BESCHEID_ERROR_MESSAGE = "Error on executing Update Bescheid Command.";
+	private static final String ERROR_MESSAGE_TEMPLATE = "Error on executing %s Command.";
 
 	private final BescheidService service;
 	private final BinaryFileService fileService;
 	private final NachrichtService nachrichtService;
 	private final AttachedItemService attachedItemService;
+	private final DocumentService documentService;
 
 	private final ApplicationEventPublisher eventPublisher;
 	private final CurrentUserService userService;
@@ -85,27 +99,13 @@ class BescheidEventListener {
 
 	@EventListener(condition = IS_CREATE_BESCHEID)
 	public void onCreateBescheidCommand(CommandCreatedEvent event) {
-		Command command = event.getSource();
-
-		doCreateBescheid(command);
-	}
-
-	private void doCreateBescheid(Command command) {
-		SecurityContext prevContext = null;
-		try {
-			prevContext = userService.startSecurityContext(command);
-			execute(command);
-		} catch (Exception e) {
-			LOG.error(LOG_MESSAGE_TEMPLATE, CREATE_BESCHEID_ERROR_MESSAGE, e);
-			eventPublisher.publishEvent(new CommandFailedEvent(command.getId(), buildErrorMessage(CREATE_BESCHEID_ERROR_MESSAGE, e)));
-		} finally {
-			userService.resetSecurityContext(prevContext);
-		}
+		runWithSecurityContext(event.getSource(), this::doCreateBescheid);
 	}
 
-	void execute(Command command) {
+	void doCreateBescheid(Command command) {
 		if (isKielEnvironment()) {
-			doCreateBescheidBiz(command);
+			var bescheid = doCreateBescheidBiz(command);
+			nachrichtService.createNachrichtDraft(bescheid);
 			eventPublisher.publishEvent(new BescheidCreatedEvent(command));
 			return;
 		}
@@ -115,15 +115,63 @@ class BescheidEventListener {
 
 	boolean isKielEnvironment() {
 		Predicate<SmartDocumentsProperties> configuredForKiel = properties -> TEMPLATE_GROUP_KIEL.equals(properties.getTemplateGroup());
-		var smartDocumentsProperties1 = smartDocumentsProperties.filter(configuredForKiel);
-		return smartDocumentsProperties1.isPresent();
+		return smartDocumentsProperties.filter(configuredForKiel).isPresent();
+	}
+
+	@EventListener(condition = IS_DELETE_BESCHEID)
+	public void onDeleteBescheid(CommandCreatedEvent event) {
+		runWithSecurityContext(event.getSource(), this::doDeleteBescheid);
+	}
+
+	void doDeleteBescheid(Command command) {
+		attachedItemService.deleteBescheidDraft(command);
+		eventPublisher.publishEvent(new BescheidDeletedEvent(command));
+	}
+
+	@EventListener(condition = IS_UPDATE_BESCHEID)
+	public void onUpdateBescheidCommand(CommandCreatedEvent event) {
+		runWithSecurityContext(event.getSource(), this::doUpdateBescheid);
+	}
+
+	void doUpdateBescheid(Command command) {
+		attachedItemService.updateBescheidDraft(command);
+		eventPublisher.publishEvent(new BescheidUpdatedEvent(command));
 	}
 
-	public void doCreateBescheidBiz(@NonNull Command command) {
+	@EventListener(condition = IS_CREATE_BESCHEID_DOCUMENT)
+	public void onCreatedBescheidDocument(CommandCreatedEvent event) {
+		runWithSecurityContext(event.getSource(), this::doCreateBescheidDocument);
+	}
+
+	void doCreateBescheidDocument(Command command) {
+		var bescheid = doCreateBescheidBiz(command);
+		var bescheidDocument = documentService.createBescheidDocument(command, bescheid);
+		eventPublisher.publishEvent(new BescheidDocumentCreatedEvent(command, bescheidDocument));
+	}
+
+	Bescheid doCreateBescheidBiz(@NonNull Command command) {
 		var bescheid = service.createBescheid(createRequest(command));
-		bescheid = fileService.uploadBescheidFile(bescheid);
+		return fileService.uploadBescheidFile(bescheid);
+	}
+
+	@EventListener(condition = IS_SEND_BESCHEID)
+	public void onSendBescheidCommand(CommandCreatedEvent event) {
+		runWithSecurityContext(event.getSource(), this::doSendBescheid);
+	}
+
+	void doSendBescheid(Command command) {
+		service.sendBescheidManually(command.getRelationId(), command.getRelationVersion());
+		eventPublisher.publishEvent(new BescheidSentEvent(command));
+	}
 
-		nachrichtService.createNachrichtDraft(bescheid);
+	@EventListener(condition = IS_SEND_POSTFACH_MAIL)
+	public void onSendPostfachMailCommand(CommandCreatedEvent event) {
+		runWithSecurityContext(event.getSource(), this::doSendPostfachMail);
+	}
+
+	void doSendPostfachMail(Command command) {
+		service.sendBescheidPostfachMail(command.getRelationId(), command.getRelationVersion());
+		eventPublisher.publishEvent(new BescheidSentEvent(command));
 	}
 
 	BescheidRequest createRequest(Command command) {
@@ -141,36 +189,20 @@ class BescheidEventListener {
 		return builder.build();
 	}
 
-	@EventListener(condition = IS_DELETE_BESCHEID)
-	public void onDeleteBescheid(CommandCreatedEvent event) {
-		SecurityContext prevContext = null;
-		Command command = event.getSource();
-		try {
-			prevContext = userService.startSecurityContext(command);
-			attachedItemService.deleteBescheidDraft(command);
-			eventPublisher.publishEvent(new BescheidDeletedEvent(command));
-		} catch (Exception e) {
-			LOG.error(LOG_MESSAGE_TEMPLATE, DELETE_BESCHEID_ERROR_MESSAGE, e);
-			eventPublisher.publishEvent(new CommandFailedEvent(command.getId(), buildErrorMessage(DELETE_BESCHEID_ERROR_MESSAGE, e)));
-		} finally {
-			userService.resetSecurityContext(prevContext);
-		}
-	}
-	@EventListener(condition = IS_UPDATE_BESCHEID)
-	public void onUpdateBescheidCommand(CommandCreatedEvent event) {
-		Command command = event.getSource();
+	void runWithSecurityContext(Command command, Consumer<Command> commandExecutor) {
 		SecurityContext prevContext = null;
 		try {
 			prevContext = userService.startSecurityContext(command);
-			attachedItemService.updateBescheidDraft(command);
-			eventPublisher.publishEvent(new BescheidUpdatedEvent(command));
+			commandExecutor.accept(command);
 		} catch (Exception e) {
-			LOG.error(LOG_MESSAGE_TEMPLATE, UPDATE_BESCHEID_ERROR_MESSAGE, e);
-			eventPublisher.publishEvent(new CommandFailedEvent(command.getId(), buildErrorMessage(UPDATE_BESCHEID_ERROR_MESSAGE, e)));
+			var errorMessage = ERROR_MESSAGE_TEMPLATE.formatted(command.getOrder());
+			LOG.error(LOG_MESSAGE_TEMPLATE, errorMessage, e);
+			eventPublisher.publishEvent(new CommandFailedEvent(command.getId(), buildErrorMessage(errorMessage, e)));
 		} finally {
 			userService.resetSecurityContext(prevContext);
 		}
 	}
+
 	private String buildErrorMessage(String message, Exception cause) {
 		try {
 			StringBuilder sb = new StringBuilder(message);
@@ -182,7 +214,7 @@ class BescheidEventListener {
 			return sb.toString();
 		} catch (Exception e2) {
 			LOG.error("Error in building Error Message (sick).", e2);
-			return CREATE_BESCHEID_ERROR_MESSAGE;
+			return message;
 		}
 	}
 
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..538ae8150df7b0214c8a4cfdb619d1cb1cda3305
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import lombok.Getter;
+
+@Configuration
+@ConfigurationProperties(prefix = "ozgcloud.feature.bescheid")
+@Getter
+public class BescheidFeatureProperties {
+
+	private boolean storeAsDocument = false;
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidSentEvent.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidSentEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..8bfb22f6eec2e1cdee3bef6f7414dedfad82bd26
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidSentEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid;
+
+import de.ozgcloud.command.Command;
+import de.ozgcloud.command.CommandExecutedEvent;
+
+public class BescheidSentEvent extends CommandExecutedEvent {
+
+	public BescheidSentEvent(final Command command) {
+		super(command);
+	}
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java
index 3ee94d186e7973d2bfd1a484688cfb223be23e55..8eb392fbf673fc706751da2b68f3ef27df5fdefb 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java
@@ -1,34 +1,46 @@
 package de.ozgcloud.bescheid;
 
-import java.util.Objects;
+import java.util.Map;
+import java.util.Optional;
 
 import jakarta.annotation.PostConstruct;
 
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 
+import de.ozgcloud.bescheid.attacheditem.AttachedItem;
+import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
+import de.ozgcloud.bescheid.attributes.ClientAttributeService;
+import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.bescheid.nachricht.NachrichtService;
+import de.ozgcloud.bescheid.vorgang.Vorgang;
+import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.bescheid.vorgang.VorgangService;
+import de.ozgcloud.common.binaryfile.FileId;
 import de.ozgcloud.common.errorhandling.TechnicalException;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
 
 @Service
 @Log4j2
+@RequiredArgsConstructor
 class BescheidService {
 
-	private static final String ERROR_MESSAGE_NO_SERVICE = "'CREATE_BESCHEID' Command received but no Bescheid Endpoint is configured.";
+	private static final String ERROR_MESSAGE_NO_SERVICE = "No Bescheid Endpoint is configured.";
 
-	@Autowired
-	private VorgangService vorgangService;
-	@Autowired(required = false)
-	private BescheidRemoteService remoteService;
+	private final VorgangService vorgangService;
+	private final AttachedItemService attachedItemService;
+	private final NachrichtService nachrichtService;
+	private final CurrentUserService currentUserService;
+	private final ClientAttributeService bescheidClientAttributeService;
+	private final Optional<BescheidRemoteService> remoteService;
 
 	@PostConstruct
 	void logStatus() {
-		if (Objects.isNull(ERROR_MESSAGE_NO_SERVICE)) {
-			LOG.info("No BescheidRemoteService configured - Bescheid creation is not possible.");
-		} else {
-			LOG.info("Bescheid-Manager is configured.");
-		}
+		remoteService.ifPresentOrElse(
+				service -> LOG.info("No BescheidRemoteService configured - Bescheid creation is not possible."),
+				() -> LOG.info("Bescheid-Manager is configured."));
 	}
 
 	public Bescheid createBescheid(BescheidRequest request) {
@@ -39,16 +51,108 @@ class BescheidService {
 
 	private Bescheid doCreateBescheid(BescheidRequest request) {
 		var vorgang = vorgangService.getById(request.getVorgangId());
-		return remoteService.create(request, vorgang)
+		return remoteService.get().create(request, vorgang)
 				.toBuilder().vorgangId(vorgang.getId()).serviceKonto(vorgang.getServiceKonto())
 				.build();
 	}
 
 	private void checkRemoteService() {
-		if (Objects.isNull(remoteService)) {
+		if (remoteService.isEmpty()) {
 			LOG.error(ERROR_MESSAGE_NO_SERVICE);
 			throw new TechnicalException(ERROR_MESSAGE_NO_SERVICE);
 		}
 	}
 
+	public void sendBescheidManually(String id, long version) {
+		var bescheidItem = attachedItemService.getItem(id);
+		validateBescheidSendManually(bescheidItem, version);
+		sendBescheid(bescheidItem);
+	}
+
+	void validateBescheidSendManually(AttachedItem bescheidItem, long version) {
+		validateBescheid(bescheidItem, version);
+		var sendBy = bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY);
+		if (Bescheid.SendBy.MANUAL.notValue(sendBy)) {
+			throw new TechnicalException("Bescheid has unexpected sendBy value: '%s'. Expected is %s" .formatted(sendBy, Bescheid.SendBy.MANUAL));
+		}
+	}
+
+	public void sendBescheidPostfachMail(String id, long version) {
+		var bescheidItem = attachedItemService.getItem(id);
+		validateBescheidSendPostfach(bescheidItem, version);
+		var vorgang = vorgangService.getById(VorgangId.from(bescheidItem.getVorgangId()));
+		nachrichtService.createNachrichtDraft(buildBescheid(bescheidItem, vorgang.getServiceKonto()));
+		sendBescheid(bescheidItem);
+	}
+
+	public void validateBescheidSendPostfach(AttachedItem bescheidItem, long version) {
+		validateBescheid(bescheidItem, version);
+		var sendBy = bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY);
+		if (Bescheid.SendBy.NACHRICHT.notValue(sendBy)) {
+			throw new TechnicalException("Bescheid has unexpected sendBy value: '%s'. Expected is %s".formatted(sendBy, Bescheid.SendBy.NACHRICHT));
+		}
+		if (StringUtils.isBlank(getNachrichtSubject(bescheidItem))) {
+			throw new TechnicalException("Bescheid has no nachricht subject");
+		}
+		if (StringUtils.isBlank(getNachrichtText(bescheidItem))) {
+			throw new TechnicalException("Bescheid has no nachricht text");
+		}
+	}
+
+	void validateBescheid(AttachedItem bescheidItem, long version) {
+		if (bescheidItem.getVersion() != version) {
+			throw new TechnicalException("Bescheid has different version. Expected: %d, but was: %d" .formatted(version, bescheidItem.getVersion()));
+		}
+		var status = MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_STATUS);
+		if (Bescheid.Status.DRAFT.not(status)) {
+			throw new TechnicalException("Bescheid has status '%s'. Bescheid must have status DRAFT" .formatted(status));
+		}
+		if (StringUtils.isBlank(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT))) {
+			throw new TechnicalException("Bescheid has no document");
+		}
+	}
+
+	Bescheid buildBescheid(AttachedItem bescheidItem, Vorgang.ServiceKonto serviceKonto) {
+		return Bescheid.builder()
+				.vorgangId(VorgangId.from(bescheidItem.getVorgangId()))
+				.genehmigt(getBewilligt(bescheidItem))
+				.bescheidFileId(FileId.from(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT)))
+				.nachrichtSubject(Optional.ofNullable(getNachrichtSubject(bescheidItem)))
+				.nachrichtText(Optional.ofNullable(getNachrichtText(bescheidItem)))
+				.createdBy(currentUserService.getUserProfile().getId())
+				.serviceKonto(serviceKonto)
+				.build();
+	}
+
+	String getNachrichtSubject(AttachedItem bescheidItem) {
+		return MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_SUBJECT);
+	}
+
+	String getNachrichtText(AttachedItem bescheidItem) {
+		return MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_TEXT);
+	}
+
+	void sendBescheid(AttachedItem bescheidItem) {
+		attachedItemService.patch(setBescheidSendStatus(bescheidItem));
+		try {
+			vorgangService.bescheiden(bescheidItem.getVorgangId());
+		} catch (Exception e) {
+			var item = attachedItemService.getItem(bescheidItem.getId());
+			attachedItemService.patch(setBescheidDraftStatus(item));
+			throw e;
+		}
+		bescheidClientAttributeService.setAntragResult(bescheidItem.getVorgangId(), getBewilligt(bescheidItem));
+	}
+
+	boolean getBewilligt(AttachedItem bescheidItem) {
+		return MapUtils.getBooleanValue(bescheidItem.getItem(), Bescheid.FIELD_BEWILLIGT, false);
+	}
+
+	AttachedItem setBescheidSendStatus(AttachedItem bescheidItem) {
+		return bescheidItem.toBuilder().item(Map.of(Bescheid.FIELD_STATUS, Bescheid.Status.SEND.name())).build();
+	}
+
+	AttachedItem setBescheidDraftStatus(AttachedItem bescheidItem) {
+		return bescheidItem.toBuilder().item(Map.of(Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name())).build();
+	}
 }
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItem.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3407d72ea4d4a13e26d68571dc329ad174dcdbd
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attacheditem;
+
+import java.util.Map;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+@Builder(toBuilder = true)
+@Getter
+public class AttachedItem {
+
+	public static final String PROPERTY_ID = "id";
+	public static final String PROPERTY_CLIENT = "client";
+	public static final String PROPERTY_VORGANG_ID = "vorgangId";
+	public static final String PROPERTY_ITEM_NAME = "itemName";
+	public static final String PROPERTY_VERSION = "version";
+	public static final String PROPERTY_ITEM = "item";
+
+	private String id;
+	@Builder.Default
+	private long version = 0L;
+
+	private String client;
+	private String vorgangId;
+	private String itemName;
+
+	@Singular("itemEntry")
+	private Map<String, Object> item;
+
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapper.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2b373a33138cc4df45ea98c5f516921713f4863
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attacheditem;
+
+import org.mapstruct.Mapper;
+
+import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper;
+import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItem;
+
+@Mapper(uses = { GrpcObjectMapper.class})
+interface AttachedItemMapper {
+
+	AttachedItem mapFromVorgangAttachedItem(GrpcVorgangAttachedItem item);
+
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java
index cf8d8bf0e80e6a905b5e2c5097382462744acf74..0b2b6340757011822e3bb0778d23a66d22851f8b 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java
@@ -47,10 +47,11 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class AttachedItemService {
 
-	static final String BESCHEID_ITEM_NAME = "Bescheid";
+	public static final String BESCHEID_ITEM_NAME = "Bescheid";
 	static final String CREATE_ATTACHED_ITEM_ORDER = "CREATE_ATTACHED_ITEM";
 	static final String UPDATE_ATTACHED_ITEM_ORDER = "UPDATE_ATTACHED_ITEM";
 	static final String DELETE_ATTACHED_ITEM = "DELETE_ATTACHED_ITEM";
+	static final String PATCH_ATTACHED_ITEM = "PATCH_ATTACHED_ITEM";
 
 	private static final Predicate<String> notExpectedSendByValue = sendBy -> !BescheidItem.ACCEPTED_SEND_BY_VALUES.contains(sendBy);
 
@@ -151,14 +152,14 @@ public class AttachedItemService {
 
 	void validateBescheidData(Map<String, Object> bodyObject) {
 		if (isNull(bodyObject.get(BescheidItem.FIELD_BESCHIEDEN_AM))) {
-			throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(BescheidItem.FIELD_BESCHIEDEN_AM));
+			throw new TechnicalException("Fields '%s' is required for bescheid creation" .formatted(BescheidItem.FIELD_BESCHIEDEN_AM));
 		}
 		if (isNull(bodyObject.get(BescheidItem.FIELD_BEWILLIGT))) {
-			throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(BescheidItem.FIELD_BEWILLIGT));
+			throw new TechnicalException("Fields '%s' is required for bescheid creation" .formatted(BescheidItem.FIELD_BEWILLIGT));
 		}
 		Optional.ofNullable(MapUtils.getString(bodyObject, BescheidItem.FIELD_SEND_BY)).filter(notExpectedSendByValue)
 				.ifPresent(sendBy ->
-						LOG.warn("Unexpected value for field '%s': %s. Allowed are: %s".formatted(BescheidItem.FIELD_SEND_BY, sendBy,
+						LOG.warn("Unexpected value for field '%s': %s. Allowed are: %s" .formatted(BescheidItem.FIELD_SEND_BY, sendBy,
 								String.join(", ", BescheidItem.ACCEPTED_SEND_BY_VALUES)))
 				);
 	}
@@ -172,7 +173,7 @@ public class AttachedItemService {
 	void validateBescheidStatus(BescheidItem bescheid) {
 		var bescheidStatus = MapUtils.getString(bescheid.getBescheidData(), BescheidItem.FIELD_STATUS);
 		if (BescheidItem.Status.DRAFT.not(bescheidStatus)) {
-			throw new TechnicalException("Bescheid draft with ID '%s' has an unexpected status: '%s'".formatted(bescheid.getId(), bescheidStatus));
+			throw new TechnicalException("Bescheid draft with ID '%s' has an unexpected status: '%s'" .formatted(bescheid.getId(), bescheidStatus));
 		}
 	}
 
@@ -185,4 +186,32 @@ public class AttachedItemService {
 				.build();
 	}
 
+	public AttachedItem getItem(String id) {
+		return remoteService.getItem(id);
+	}
+
+	public void patch(AttachedItem item) {
+		commandService.createAndWaitUntilDone(buildPatchBescheidCommand(item));
+	}
+
+	OzgCloudCommand buildPatchBescheidCommand(AttachedItem bescheidItem) {
+		return OzgCloudCommand.builder()
+				.vorgangId(commandMapper.toOzgCloudVorgangId(bescheidItem.getVorgangId()))
+				.relationId(commandMapper.mapRelationId(bescheidItem.getId()))
+				.relationVersion(bescheidItem.getVersion())
+				.order(PATCH_ATTACHED_ITEM)
+				.bodyObject(buildObjectMap(bescheidItem))
+				.build();
+	}
+
+	Map<String, Object> buildObjectMap(AttachedItem bescheidItem) {
+		var bodyObject = new HashMap<String, Object>();
+		bodyObject.put(AttachedItem.PROPERTY_ID, bescheidItem.getId());
+		bodyObject.put(AttachedItem.PROPERTY_CLIENT, bescheidItem.getClient());
+		bodyObject.put(AttachedItem.PROPERTY_VORGANG_ID, bescheidItem.getVorgangId());
+		bodyObject.put(AttachedItem.PROPERTY_ITEM_NAME, bescheidItem.getItemName());
+		bodyObject.put(AttachedItem.PROPERTY_VERSION, bescheidItem.getVersion());
+		bodyObject.put(AttachedItem.PROPERTY_ITEM, bescheidItem.getItem());
+		return bodyObject;
+	}
 }
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItem.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItem.java
index 84b6f8aff6a040ab619ccaed13d19c08c7d3b4f7..a018068459db579d4c480c4bf7223919d848fd6c 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItem.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItem.java
@@ -31,6 +31,7 @@ import lombok.Getter;
 
 @Builder(toBuilder = true)
 @Getter
+@Deprecated(since = "2.6.0")
 public class BescheidItem {
 
 	static final Set<String> ACCEPTED_SEND_BY_VALUES = Set.of("POSTFACH", "MANUAL");
@@ -62,7 +63,7 @@ public class BescheidItem {
 	private Map<String, Object> bescheidData;
 
 	public enum Status {
-		DRAFT, BESCHEID;
+		DRAFT, BESCHEID, SEND;
 
 		public boolean not(String value) {
 			return !hasValue(value);
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItemMapper.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItemMapper.java
index ff92f8c2f43b6525dbac9a44db3e573c4931e62e..2c915d65d934abab2636433b02e30a1eaf8e764d 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItemMapper.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/BescheidItemMapper.java
@@ -42,6 +42,7 @@ import lombok.RequiredArgsConstructor;
 
 @Component
 @RequiredArgsConstructor
+@Deprecated(since = "2.6.0")
 public class BescheidItemMapper {
 
 	private final GrpcObjectMapper grpcObjectMapper;
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteService.java
index e0ee53970c6e8b22f1e66b9060e7cd505847b987..aa6709149d8d968e263c86e0197e281ff99c3a8e 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteService.java
@@ -57,6 +57,8 @@ class VorgangAttachedItemRemoteService {
 	private ClientInterceptor bescheidCallContextInterceptor;
 	@Autowired
 	private BescheidItemMapper bescheidItemMapper;
+	@Autowired
+	private AttachedItemMapper attachedItemMapper;
 
 	public Optional<BescheidItem> findBescheidDraft(String vorgangId) {
 		return findBescheidDraft(buildFindRequest(vorgangId));
@@ -90,6 +92,11 @@ class VorgangAttachedItemRemoteService {
 		return bescheidItemMapper.mapFromVorgangAttachedItem(grpcVorgangAttachedItemResponse.getVorgangAttachedItem());
 	}
 
+	public AttachedItem getItem(String id) {
+		var grpcVorgangAttachedItemResponse = getServiceStub().getById(buildGetByIdRequest(id));
+		return attachedItemMapper.mapFromVorgangAttachedItem(grpcVorgangAttachedItemResponse.getVorgangAttachedItem());
+	}
+
 	GrpcVorgangAttachedItemRequest buildGetByIdRequest(String bescheidId) {
 		return GrpcVorgangAttachedItemRequest.newBuilder().setId(bescheidId).build();
 	}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d87ae7496591bc23df9ba65148ca8c5f2ba24559
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attributes;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor;
+import de.ozgcloud.vorgang.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcAccessPermission;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcClientAttribute;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcClientAttributeValue;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcSetClientAttributeRequest;
+import io.grpc.ClientInterceptor;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+
+@Service("bescheid_ClientAttributeRemoteService")
+class ClientAttributeRemoteService {
+
+	@GrpcClient("vorgang-manager")
+	private ClientAttributeServiceBlockingStub serviceBlockingStub;
+
+	@Autowired
+	private ClientInterceptor bescheidCallContextInterceptor;
+
+	public void setBooleanReadOnlyClientAttribute(String vorgangId, String attributeName, boolean value) {
+		serviceBlockingStub.withInterceptors(bescheidCallContextInterceptor).set(buildRequest(vorgangId, attributeName, value));
+	}
+
+	GrpcSetClientAttributeRequest buildRequest(String vorgangId, String attributeName, boolean value) {
+		return GrpcSetClientAttributeRequest.newBuilder()
+				.setVorgangId(vorgangId)
+				.setAttribute(buildClientAttribute(attributeName, value))
+				.build();
+	}
+
+	GrpcClientAttribute buildClientAttribute(String attributeName, boolean value) {
+		return GrpcClientAttribute.newBuilder()
+				.setClientName(BescheidCallContextAttachingInterceptor.BESCHEID_MANAGER_CLIENT_NAME)
+				.setAccess(GrpcAccessPermission.READ_ONLY)
+				.setAttributeName(attributeName)
+				.setValue(GrpcClientAttributeValue.newBuilder().setBoolValue(value).build())
+				.build();
+	}
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeService.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bd31a95ad2bcafe9628557b233b0cb5799f0060
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attributes/ClientAttributeService.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attributes;
+
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+
+@Service("bescheid_ClientAttributeService")
+@RequiredArgsConstructor
+public class ClientAttributeService {
+
+	public static final String ATTRIBUTE_NAME_ANTRAG_BEWILLIGT = "ANTRAG_BEWILLIGT";
+
+	private final ClientAttributeRemoteService bescheidClientAttributeRemoteService;
+
+	public void setAntragResult(String vorgangId, boolean antragResult) {
+		bescheidClientAttributeRemoteService.setBooleanReadOnlyClientAttribute(vorgangId, ATTRIBUTE_NAME_ANTRAG_BEWILLIGT, antragResult);
+	}
+
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/dummy/DummyBescheidRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/dummy/DummyBescheidRemoteService.java
index 403defced86ffae1733612770f145c5f1581ca27..7a1152e6663a98cb91510d776b920aa3d12fa725 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/dummy/DummyBescheidRemoteService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/dummy/DummyBescheidRemoteService.java
@@ -1,6 +1,8 @@
 package de.ozgcloud.bescheid.dummy;
 
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import java.util.Optional;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.stereotype.Service;
 
 import com.google.common.net.MediaType;
@@ -12,7 +14,7 @@ import de.ozgcloud.bescheid.vorgang.Vorgang;
 import de.ozgcloud.common.binaryfile.TempFileUtils;
 
 @Service
-@ConditionalOnMissingBean(BescheidRemoteService.class)
+@ConditionalOnProperty("ozgcloud.feature.bescheid.enable-dummy-document-processor")
 class DummyBescheidRemoteService implements BescheidRemoteService {
 
 	private static final String DUMMY_BESCHEID_FILE_NAME = "dummy-bescheid.pdf";
@@ -26,6 +28,8 @@ class DummyBescheidRemoteService implements BescheidRemoteService {
 				.bescheidFile(file)
 				.bescheidFileName(DUMMY_BESCHEID_FILE_NAME)
 				.contentType(DUMMY_BESCHEID_CONTENT_TYPE)
+				.nachrichtText(Optional.of("Dummy Bescheid"))
+				.nachrichtSubject(Optional.of("Nachricht Subject"))
 				.size(file.length())
 				.createdBy(request.getCreateFor().getId())
 				.build();
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java
index a63d72018784b9b2f628a6267074f0d3345b5bd8..fc4a6e29d4b5b32820dc96b93c8d93e36eea985c 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java
@@ -38,7 +38,7 @@ public class NachrichtService {
 		return getAddress(bescheid).map(address -> Nachricht.builder()
 				.vorgangId(bescheid.getVorgangId())
 				.postfachAddress(address)
-				.subject(SUBJECT)
+				.subject(bescheid.getNachrichtSubject().orElse(SUBJECT))
 				.mailBody(buildMessage(bescheid))
 				.createdBy(bescheid.getCreatedBy())
 				.bescheidFileId(bescheid.getBescheidFileId())
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java
index 87bd9af60ce30a442052172f5b14d7aeb6f21e9d..6dd87bd4b8bb2423fe933e8d4e098e056890f8fc 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java
@@ -126,7 +126,7 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService {
 			var expr = xPath.compile("/root/SmartDocument/Fields/NachrichtenText/text()");
 			var text = (Text) expr.evaluate(document, XPathConstants.NODE);
 
-			return Optional.of(text.getTextContent());
+			return Optional.ofNullable(text.getTextContent());
 		} catch (XPathExpressionException | SAXException | IOException | ParserConfigurationException e) {
 			LOG.error("XML-Parsing error on extracting Nachricht-Text: {}", e.getMessage(), e);
 		} catch (ClassCastException e) {
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/Vorgang.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/Vorgang.java
index d266fd84953b50a95fdc3780772f17f91aaf21bc..f8f076017157263a433cd7c582153e9bdbb4ea5d 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/Vorgang.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/Vorgang.java
@@ -20,6 +20,7 @@ public class Vorgang {
 
 	@JsonIgnore
 	private VorgangId id;
+	private long version;
 
 	private String vorgangName;
 
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java
index c479421c36775b9a98db6dd5cf84106faafa68f3..15ab8a87d9ffee2ef4c5a532a924c16d2405a5da 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java
@@ -1,17 +1,46 @@
 package de.ozgcloud.bescheid.vorgang;
 
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import de.ozgcloud.apilib.common.command.OzgCloudCommand;
+import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
+import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
 import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
 
 @Service("bescheid_VorgangService")
+@RequiredArgsConstructor
 public class VorgangService {
 
-	@Autowired
-	public VorgangRemoteService remoteService;
+	static final String VORGANG_BESCHEIDEN = "VORGANG_BESCHEIDEN";
+
+	private final VorgangRemoteService remoteService;
+	private final OzgCloudCommandService commandService;
+	private final CommandMapper commandMapper;
 
 	public Vorgang getById(@NonNull VorgangId id) {
 		return remoteService.getById(id);
 	}
+
+	public void bescheiden(String vorgangId) {
+		bescheiden(getById(VorgangId.from(vorgangId)));
+	}
+
+	public void bescheiden(Vorgang vorgang) {
+		commandService.createAndWaitUntilDone(buildBescheidenCommand(vorgang));
+	}
+
+	OzgCloudCommand buildBescheidenCommand(Vorgang vorgang) {
+		return OzgCloudCommand.builder()
+				.vorgangId(commandMapper.toOzgCloudVorgangId(vorgang.getId().toString()))
+				.relationId(commandMapper.mapRelationId(vorgang.getId().toString()))
+				.relationVersion(vorgang.getVersion())
+				.order(VORGANG_BESCHEIDEN)
+				.build();
+	}
+
+	public Vorgang.ServiceKonto getServiceKonto(String vorgangId) {
+		var vorgang = getById(VorgangId.from(vorgangId));
+		return vorgang.getServiceKonto();
+	}
 }
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/document/Document.java b/bescheid-manager/src/main/java/de/ozgcloud/document/Document.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc0d09b53f1d9cbc4d3792dca94c429f46d3e5e1
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/document/Document.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+public class Document {
+
+	public static final String FIELD_DOCUMENT_TYPE = "type";
+	public static final String FIELD_DOCUMENT_FILE = "documentFile";
+	public static final String FIELD_NACHRICHT_TEXT = "nachrichtText";
+	public static final String FIELD_NACHRICHT_SUBJECT = "nachrichtSubject";
+
+	private String id;
+	private String type;
+	private String fileId;
+	private String nachrichtSubject;
+	private String nachrichtText;
+
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentGrpcService.java b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentGrpcService.java
new file mode 100644
index 0000000000000000000000000000000000000000..673935d696777f3708fafbe134a8a496541b0d02
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentGrpcService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import de.ozgcloud.document.DocumentServiceGrpc.DocumentServiceImplBase;
+import io.grpc.stub.StreamObserver;
+import lombok.RequiredArgsConstructor;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@GrpcService
+@RequiredArgsConstructor
+public class DocumentGrpcService extends DocumentServiceImplBase {
+
+	private final DocumentService documentService;
+	private final DocumentMapper documentMapper;
+
+	@Override
+	public void getDocument(GrpcGetDocumentRequest request, StreamObserver<GrpcGetDocumentResponse> responseObserver) {
+		var document = documentService.getDocument(request.getId());
+		responseObserver.onNext(buildGetDocumentResponse(document));
+		responseObserver.onCompleted();
+	}
+
+	GrpcGetDocumentResponse buildGetDocumentResponse(Document document) {
+		return GrpcGetDocumentResponse.newBuilder().setDocument(documentMapper.toGrpcDocument(document)).build();
+	}
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentMapper.java b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..41180bab88ef3c302300bd1e51ca34e3ed89619b
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentMapper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import org.apache.commons.collections.MapUtils;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+
+import de.ozgcloud.bescheid.attacheditem.AttachedItem;
+
+@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
+interface DocumentMapper {
+
+	@Mapping(target = "allFields", ignore = true)
+	@Mapping(target = "unknownFields", ignore = true)
+	@Mapping(target = "typeBytes", ignore = true)
+	@Mapping(target = "nachrichtTextBytes", ignore = true)
+	@Mapping(target = "nachrichtSubjectBytes", ignore = true)
+	@Mapping(target = "mergeUnknownFields", ignore = true)
+	@Mapping(target = "mergeFrom", ignore = true)
+	@Mapping(target = "idBytes", ignore = true)
+	@Mapping(target = "fileIdBytes", ignore = true)
+	@Mapping(target = "clearOneof", ignore = true)
+	@Mapping(target = "clearField", ignore = true)
+	GrpcDocument toGrpcDocument(Document document);
+
+	default Document fromAttachedItem(AttachedItem attachedItem) {
+		return Document.builder()
+				.id(attachedItem.getId())
+				.type(MapUtils.getString(attachedItem.getItem(), Document.FIELD_DOCUMENT_TYPE))
+				.fileId(MapUtils.getString(attachedItem.getItem(), Document.FIELD_DOCUMENT_FILE))
+				.nachrichtText(MapUtils.getString(attachedItem.getItem(), Document.FIELD_NACHRICHT_TEXT))
+				.nachrichtSubject(MapUtils.getString(attachedItem.getItem(), Document.FIELD_NACHRICHT_SUBJECT))
+				.build();
+	}
+}
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java
index 0a4e4090ec4f656f47d34d612a1cd72ef3474c56..25838cf4939ce8d9b2db28ac1f2bf404a83304e7 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java
@@ -23,16 +23,20 @@
  */
 package de.ozgcloud.document;
 
+import static java.util.Objects.*;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 
 import de.ozgcloud.apilib.common.command.OzgCloudCommand;
 import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
 import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
+import de.ozgcloud.bescheid.Bescheid;
 import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor;
 import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
 import de.ozgcloud.bescheid.attacheditem.BescheidItem;
@@ -44,19 +48,30 @@ import lombok.RequiredArgsConstructor;
 @RequiredArgsConstructor
 public class DocumentService {
 
+	public static final String DOCUMENT_ITEM_NAME = "Document";
+	public static final String DOCUMENT_TYPE = "BESCHEID";
+
 	static final String CREATE_ATTACHED_ITEM_ORDER = "CREATE_ATTACHED_ITEM";
-	static final String DOCUMENT_ITEM_NAME = "Document";
 	static final String FIELD_DOCUMENT_TYPE = "type";
-	static final String DOCUMENT_TYPE = "BESCHEID";
 	static final String FIELD_DOCUMENT_FILE = "documentFile";
+	static final String FIELD_NACHRICHT_TEXT = "nachrichtText";
 
 	private final AttachedItemService attachedItemService;
 	private final OzgCloudCommandService commandService;
 	private final CommandMapper commandMapper;
+	private final DocumentMapper documentMapper;
 
 	public String createBescheidDocument(Command command) {
+		return createBescheidDocument(command, buildItemMap(command));
+	}
+
+	public String createBescheidDocument(Command command, Bescheid bescheid) {
+		return createBescheidDocument(command, buildItemMap(bescheid));
+	}
+
+	String createBescheidDocument(Command command, Map<String, Object> itemMap) {
 		validateBescheidItem(command.getRelationId());
-		var ozgCloudCommand = buildCreateDocumentOzgCommand(command);
+		var ozgCloudCommand = buildCreateDocumentOzgCommand(command, buildAttachedItem(command, itemMap));
 		var executedCommand = commandService.createAndWaitUntilDone(ozgCloudCommand);
 		return executedCommand.getCreatedResource();
 	}
@@ -69,21 +84,21 @@ public class DocumentService {
 		}
 	}
 
-	OzgCloudCommand buildCreateDocumentOzgCommand(Command command) {
+	OzgCloudCommand buildCreateDocumentOzgCommand(Command command, Map<String, Object> bodyObject) {
 		return OzgCloudCommand.builder()
 				.order(CREATE_ATTACHED_ITEM_ORDER)
 				.vorgangId(commandMapper.toOzgCloudVorgangId(command.getVorgangId()))
 				.relationId(commandMapper.mapRelationId(command.getVorgangId()))
-				.bodyObject(buildAttachedItem(command))
+				.bodyObject(bodyObject)
 				.build();
 	}
 
-	Map<String, Object> buildAttachedItem(Command command) {
+	Map<String, Object> buildAttachedItem(Command command, Map<String, Object> itemMap) {
 		var result = new HashMap<String, Object>();
 		result.put(BescheidItem.PROPERTY_VORGANG_ID, command.getVorgangId());
 		result.put(BescheidItem.PROPERTY_CLIENT, BescheidCallContextAttachingInterceptor.BESCHEID_MANAGER_CLIENT_NAME);
 		result.put(BescheidItem.PROPERTY_ITEM_NAME, DOCUMENT_ITEM_NAME);
-		result.put(BescheidItem.PROPERTY_ITEM, buildItemMap(command));
+		result.put(BescheidItem.PROPERTY_ITEM, itemMap);
 		return result;
 	}
 
@@ -93,4 +108,16 @@ public class DocumentService {
 		return Map.of(FIELD_DOCUMENT_TYPE, DOCUMENT_TYPE, FIELD_DOCUMENT_FILE, fileId);
 	}
 
+	Map<String, Object> buildItemMap(Bescheid bescheid) {
+		if (isNull(bescheid.getBescheidFileId())) {
+			throw new TechnicalException("Bescheid file id is missing.");
+		}
+		return Map.of(FIELD_DOCUMENT_TYPE, DOCUMENT_TYPE,
+				FIELD_DOCUMENT_FILE, bescheid.getBescheidFileId().toString(),
+				FIELD_NACHRICHT_TEXT, bescheid.getNachrichtText().orElse(StringUtils.EMPTY));
+	}
+
+	public Document getDocument(String id) {
+		return documentMapper.fromAttachedItem(attachedItemService.getItem(id));
+	}
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java
index 5964ad0da6fd99680853a85519c0958a150607ae..654825eb6bf6070b6651da59af1554a6e4669227 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java
@@ -8,8 +8,10 @@ import static org.mockito.Mockito.*;
 
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Consumer;
 
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
@@ -22,17 +24,19 @@ import org.springframework.security.core.context.SecurityContext;
 import org.springframework.test.util.ReflectionTestUtils;
 
 import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
+import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory;
 import de.ozgcloud.bescheid.binaryfile.BinaryFileService;
 import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
 import de.ozgcloud.bescheid.common.callcontext.UserProfile;
 import de.ozgcloud.bescheid.common.callcontext.UserProfileTestFactory;
 import de.ozgcloud.bescheid.nachricht.NachrichtService;
 import de.ozgcloud.bescheid.smartdocuments.SmartDocumentsProperties;
-import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandCreatedEventTestFactory;
 import de.ozgcloud.command.CommandFailedEvent;
 import de.ozgcloud.command.CommandTestFactory;
+import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.document.DocumentService;
 
 class BescheidEventListenerTest {
 
@@ -48,6 +52,10 @@ class BescheidEventListenerTest {
 	private NachrichtService nachrichtService;
 	@Mock
 	private AttachedItemService attachedItemService;
+	@Mock
+	private BescheidFeatureProperties featureProperties;
+	@Mock
+	private DocumentService documentService;
 
 	@Mock
 	private ApplicationEventPublisher eventPublisher;
@@ -57,91 +65,36 @@ class BescheidEventListenerTest {
 	@Nested
 	class TestOnCreateBescheidCommand {
 
-		private Command command = CommandTestFactory.createBuilder()
-				.bodyObject(
-						Map.of(VORGANG_ID_BODYKEY, VORGANG_ID.toString(),
-								BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING,
-								GENEHMIGT_BODYKEY, GENEHMIGT))
-				.build();
-
-		@Mock
-		private SecurityContext secContext;
+		private final Command command = CommandTestFactory.create();
 
 		@Test
-		void shouldCreateBescheid() {
-			doReturn(true).when(listener).isKielEnvironment();
-
+		void shouldCallRunWithSecurityContext() {
 			listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
 
-			verify(listener).execute(command);
+			verify(listener).runWithSecurityContext(eq(command), any());
 		}
 
 		@Test
-		void shouldPublishErrorEventOnException() {
-			doReturn(true).when(listener).isKielEnvironment();
-			doThrow(new RuntimeException("ups")).when(listener).doCreateBescheidBiz(any());
-
+		void shouldExecuteCreateBescheid() {
 			listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
 
-			verify(eventPublisher).publishEvent(any(CommandFailedEvent.class));
+			verify(listener).doCreateBescheid(command);
 		}
-
-		@Nested
-		class HandleSecurityContext {
-
-			@BeforeEach
-			void init() {
-				when(userService.startSecurityContext(any())).thenReturn(secContext);
-			}
-
-			@Test
-			void shouldStartSecurityContext() {
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
-
-				verify(userService).startSecurityContext(command);
-			}
-
-			@Test
-			void shouldResetSecurityContext() {
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
-
-				verify(userService).resetSecurityContext(secContext);
-			}
-
-			@Test
-			void shouldResetSecurityContextAfterException() {
-				doReturn(true).when(listener).isKielEnvironment();
-				doThrow(new RuntimeException("ups")).when(listener).doCreateBescheidBiz(any());
-
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
-
-				verify(userService).resetSecurityContext(secContext);
-			}
-		}
-
-		@Nested
-		class CreateBescheidRequest {
-			@Test
-			void shouldContainUserProfile() {
-				UserProfile user = UserProfileTestFactory.create();
-				when(userService.getUserProfile()).thenReturn(user);
-
-				var request = listener.createRequest(command);
-
-				assertThat(request.getCreateFor()).isSameAs(user);
-			}
-		}
-
 	}
 
 	@Nested
-	class TestExecute {
+	class TestDoCreateBescheid {
+
+		private static final Command COMMAND = CommandTestFactory.createBuilder()
+				.bodyObject(
+						Map.of(VORGANG_ID_BODYKEY, VORGANG_ID.toString(),
+								BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING,
+								GENEHMIGT_BODYKEY, GENEHMIGT))
+				.build();
 
 		@Captor
 		private ArgumentCaptor<BescheidCreatedEvent> eventCaptor;
 
-		private Command command = CommandTestFactory.create();
-
 		@Nested
 		class TestKielConfigured {
 
@@ -152,26 +105,29 @@ class BescheidEventListenerTest {
 
 			@Test
 			void shouldCallDoCreateBescheidBiz() {
-				listener.execute(command);
+				listener.doCreateBescheid(COMMAND);
 
-				verify(listener).doCreateBescheidBiz(command);
+				verify(listener).doCreateBescheidBiz(COMMAND);
+				verify(attachedItemService, never()).createBescheidDraft(any());
 			}
 
 			@Test
-			void shouldNotCallVorgangAttachedItemService() {
-				listener.execute(command);
+			void shouldCallNachrichtService() {
+				var bescheid = BescheidTestFactory.create();
+				doReturn(bescheid).when(listener).doCreateBescheidBiz(any());
 
-				verify(attachedItemService, never()).createBescheidDraft(any());
+				listener.doCreateBescheid(COMMAND);
+
+				verify(nachrichtService).createNachrichtDraft(bescheid);
 			}
 
 			@Test
-			void shouldCallPublishEvent() {
-				listener.execute(command);
+			void shouldPublishBescheidCreatedEvent() {
+				listener.doCreateBescheid(COMMAND);
 
 				verify(eventPublisher).publishEvent(eventCaptor.capture());
-				assertThat(eventCaptor.getValue().getCreatedResource()).isNull();
+				assertThat(eventCaptor.getValue().getSource()).isEqualTo(CommandTestFactory.ID);
 			}
-
 		}
 
 		@Nested
@@ -183,82 +139,40 @@ class BescheidEventListenerTest {
 			}
 
 			@Test
-			void shouldNotCallDoCreateBescheidBiz() {
-				listener.execute(command);
+			void shouldCallCreateBescheidDraft() {
+				listener.doCreateBescheid(COMMAND);
 
+				verify(attachedItemService).createBescheidDraft(COMMAND);
 				verify(listener, never()).doCreateBescheidBiz(any());
 			}
 
 			@Test
-			void shouldCallVorgangAttachedItemService() {
-				listener.execute(command);
+			@DisplayName("should publish BescheidCreatedEvent after creating BescheidDraft")
+			void shouldPublishBescheidCreatedEventWithCommand() {
+				var createdResource = "item-id";
+				when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource);
+
+				listener.doCreateBescheid(COMMAND);
 
-				verify(attachedItemService).createBescheidDraft(command);
+				verify(eventPublisher).publishEvent(eventCaptor.capture());
+				assertThat(eventCaptor.getValue().getCommand()).isSameAs(COMMAND);
 			}
 
 			@Test
-			void shouldCallPublishEvent() {
-				var expectedItemId = "createdItemId";
-				when(attachedItemService.createBescheidDraft(any())).thenReturn(expectedItemId);
+			@DisplayName("should publish BescheidCreatedEvent with created resource")
+			void shouldPublishBescheidCreatedEventWithCreatedResource() {
+				var createdResource = "item-id";
+				when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource);
 
-				listener.execute(command);
+				listener.doCreateBescheid(COMMAND);
 
 				verify(eventPublisher).publishEvent(eventCaptor.capture());
-				assertThat(eventCaptor.getValue().getCreatedResource()).isEqualTo(expectedItemId);
+				assertThat(eventCaptor.getValue().getCreatedResource()).isEqualTo(createdResource);
 			}
 		}
 
 	}
 
-	@Nested
-	class CreateBescheid {
-
-		private Command command = CommandTestFactory.createBuilder()
-				.bodyObject(
-						Map.of(VORGANG_ID_BODYKEY, VORGANG_ID.toString(),
-								BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING,
-								GENEHMIGT_BODYKEY, GENEHMIGT))
-				.build();
-
-		@Captor
-		private ArgumentCaptor<BescheidRequest> requestCaptor;
-
-		private Bescheid bescheid = BescheidTestFactory.create();
-		private Bescheid bescheidWithFileId = BescheidTestFactory.create();
-
-		@BeforeEach
-		void init() {
-			when(service.createBescheid(any())).thenReturn(bescheid);
-			when(fileService.uploadBescheidFile(any())).thenReturn(bescheidWithFileId);
-			when(userService.getUserProfile()).thenReturn(UserProfileTestFactory.create());
-		}
-
-		@Test
-		void shouldCreateBescheid() {
-			listener.doCreateBescheidBiz(command);
-
-			verify(service).createBescheid(requestCaptor.capture());
-			assertThat(requestCaptor.getValue()).usingRecursiveComparison()
-					.isEqualTo(BescheidRequestTestFactory.createBuilder()
-							.vorgangId(VorgangId.from(CommandTestFactory.VORGANG_ID))
-							.build());
-		}
-
-		@Test
-		void shouldSaveFile() {
-			listener.doCreateBescheidBiz(command);
-
-			verify(fileService).uploadBescheidFile(bescheid);
-		}
-
-		@Test
-		void shouldSaveNachricht() {
-			listener.doCreateBescheidBiz(command);
-
-			verify(nachrichtService).createNachrichtDraft(bescheidWithFileId);
-		}
-	}
-
 	@Nested
 	class TestIsKielEnvironment {
 
@@ -295,149 +209,419 @@ class BescheidEventListenerTest {
 		}
 	}
 
+	@Nested
+	class TestCreateBescheidRequest {
+
+		private static final Command COMMAND = CommandTestFactory.createBuilder()
+				.vorgangId(VORGANG_ID.toString())
+				.bodyObject(
+						Map.of(BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING,
+								GENEHMIGT_BODYKEY, false))
+				.build();
+
+		@Test
+		void shouldSetVorgangId() {
+			var request = createBescheidRequest();
+
+			assertThat(request.getVorgangId()).isEqualTo(VORGANG_ID);
+		}
+
+		@Test
+		void shouldSetBescheidVom() {
+			var request = createBescheidRequest();
+
+			assertThat(request.getBescheidVom()).isEqualTo(BESCHEID_VOM);
+		}
+
+		@Test
+		void shouldSetGenehmigt() {
+			var request = createBescheidRequest();
+
+			assertThat(request.isGenehmigt()).isFalse();
+		}
+
+		@Test
+		void shouldSetDefaultGenehmigt() {
+			Command command = CommandTestFactory.createBuilder()
+					.vorgangId(VORGANG_ID.toString())
+					.bodyObject(
+							Map.of(BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING))
+					.build();
+
+			var request = listener.createRequest(command);
+
+			assertThat(request.isGenehmigt()).isTrue();
+		}
+
+		@Test
+		void shouldContainUserProfile() {
+			UserProfile user = UserProfileTestFactory.create();
+			when(userService.getUserProfile()).thenReturn(user);
+
+			var request = listener.createRequest(COMMAND);
+
+			assertThat(request.getCreateFor()).isSameAs(user);
+		}
+
+		private BescheidRequest createBescheidRequest() {
+			return listener.createRequest(COMMAND);
+		}
+	}
+
 	@Nested
 	class TestOnDeleteBescheid {
 
+		private final Command command = CommandTestFactory.create();
+
+		@Test
+		void shouldCallRunWithSecurityContext() {
+			listener.onDeleteBescheid(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).runWithSecurityContext(eq(command), any());
+		}
+
+		@Test
+		void shouldExecuteCreateBescheid() {
+			listener.onDeleteBescheid(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).doDeleteBescheid(command);
+		}
+	}
+
+	@Nested
+	class TestDoDeleteBescheid {
+
 		@Captor
 		private ArgumentCaptor<BescheidDeletedEvent> bescheidDeletedEventCaptor;
-		@Captor
-		private ArgumentCaptor<CommandFailedEvent> commandFailedEventCaptor;
 
-		private Command command = CommandTestFactory.create();
+		private final Command command = CommandTestFactory.create();
 
 		@Test
 		void shouldCallAttachedItemService() {
-			listener.onDeleteBescheid(CommandCreatedEventTestFactory.withCommand(command));
+			listener.doDeleteBescheid(command);
 
 			verify(attachedItemService).deleteBescheidDraft(command);
 		}
 
 		@Test
 		void shouldPublishCommandExecutedEvent() {
-			listener.onDeleteBescheid(CommandCreatedEventTestFactory.withCommand(command));
+			listener.doDeleteBescheid(command);
 
 			verify(eventPublisher).publishEvent(bescheidDeletedEventCaptor.capture());
 			assertThat(bescheidDeletedEventCaptor.getValue().getCommand()).isSameAs(command);
 		}
 
+	}
+
+	@Nested
+	class TestOnUpdateBescheid {
+
+		private final Command command = CommandTestFactory.create();
+
 		@Test
-		void shouldPublishErrorEventOnException() {
-			doThrow(new RuntimeException("ups")).when(attachedItemService).deleteBescheidDraft(any());
+		void shouldCallRunWithSecurityContext() {
+			listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
 
-			listener.onDeleteBescheid(CommandCreatedEventTestFactory.withCommand(command));
+			verify(listener).runWithSecurityContext(eq(command), any());
+		}
 
-			verify(eventPublisher).publishEvent(commandFailedEventCaptor.capture());
-			assertThat(commandFailedEventCaptor.getValue().getSource()).isEqualTo(command.getId());
-			assertThat(commandFailedEventCaptor.getValue().getErrorMessage()).isNotEmpty();
+		@Test
+		void shouldExecuteCreateBescheid() {
+			listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).doUpdateBescheid(command);
 		}
 
-		@Nested
-		class HandleSecurityContext {
+	}
 
-			@Mock
-			private SecurityContext secContext;
+	@Nested
+	class TestDoUpdateBescheid {
 
-			@BeforeEach
-			void init() {
-				when(userService.startSecurityContext(any())).thenReturn(secContext);
-			}
+		@Captor
+		private ArgumentCaptor<BescheidUpdatedEvent> bescheidUpdatedEventCaptor;
 
-			@Test
-			void shouldStartSecurityContext() {
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+		private final Command command = CommandTestFactory.createBuilder().build();
 
-				verify(userService).startSecurityContext(command);
-			}
+		@Test
+		void shouldCallUpdateBescheidDraft() {
+			listener.doUpdateBescheid(command);
 
-			@Test
-			void shouldResetSecurityContext() {
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+			verify(attachedItemService).updateBescheidDraft(command);
+		}
 
-				verify(userService).resetSecurityContext(secContext);
-			}
+		@Test
+		void shouldPublishBescheidUpdatedEvent() {
+			listener.doUpdateBescheid(command);
 
-			@Test
-			void shouldResetSecurityContextAfterException() {
-				doReturn(true).when(listener).isKielEnvironment();
-				doThrow(new RuntimeException("ups")).when(listener).doCreateBescheidBiz(any());
+			verify(eventPublisher).publishEvent(bescheidUpdatedEventCaptor.capture());
+			assertThat(bescheidUpdatedEventCaptor.getValue().getCommand()).isSameAs(command);
+		}
 
-				listener.onCreateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+	}
 
-				verify(userService).resetSecurityContext(secContext);
-			}
+	@Nested
+	class TestOnCreateBescheidDocument {
+
+		private final Command command = CommandTestFactory.create();
+
+		@Test
+		void shouldCallRunWithSecurityContext() {
+			listener.onCreatedBescheidDocument(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).runWithSecurityContext(eq(command), any());
+		}
+
+		@Test
+		void shouldExecuteCreateBescheidDocument() {
+			listener.onCreatedBescheidDocument(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).doCreateBescheidDocument(command);
 		}
 	}
 
 	@Nested
-	class TestOnUpdateBescheid {
+	class TestDoCreateBescheidDocument {
+
+		private static final Command COMMAND = CommandTestFactory.create();
+		private static final Bescheid BESCHEID = BescheidTestFactory.create();
 
 		@Captor
-		private ArgumentCaptor<BescheidUpdatedEvent> bescheidUpdatedEventCaptor;
+		private ArgumentCaptor<BescheidDocumentCreatedEvent> eventCaptor;
+
+		@BeforeEach
+		void init() {
+			doReturn(BESCHEID).when(listener).doCreateBescheidBiz(any());
+		}
+
+		@Test
+		void shouldCallCreateBescheidBiz() {
+			listener.doCreateBescheidDocument(COMMAND);
+
+			verify(listener).doCreateBescheidBiz(COMMAND);
+		}
+
+		@Test
+		void shouldCallCreateBescheidDocument() {
+			listener.doCreateBescheidDocument(COMMAND);
+
+			verify(documentService).createBescheidDocument(COMMAND, BESCHEID);
+		}
+
+		@Test
+		@DisplayName("should publish BescheidDocumentCreatedEvent with command")
+		void shouldPublishEventWithCommand() {
+			listener.doCreateBescheidDocument(COMMAND);
+
+			verify(eventPublisher).publishEvent(eventCaptor.capture());
+			assertThat(eventCaptor.getValue().getCommand()).isEqualTo(COMMAND);
+		}
+
+		@Test
+		@DisplayName("should publish BescheidDocumentCreatedEvent with createdResource")
+		void shouldPublishEventWithCreatedResource() {
+			var bescheidDocument = "document-id";
+			when(documentService.createBescheidDocument(any(), any(Bescheid.class))).thenReturn(bescheidDocument);
+
+			listener.doCreateBescheidDocument(COMMAND);
+
+			verify(eventPublisher).publishEvent(eventCaptor.capture());
+			assertThat(eventCaptor.getValue().getCreatedResource()).isEqualTo(bescheidDocument);
+		}
+	}
+
+	@Nested
+	class TestCreateBescheidBiz {
+
+		private final Command command = CommandTestFactory.create();
+
+		@Test
+		void shouldCallCreateRequest() {
+			listener.doCreateBescheidBiz(command);
+
+			verify(listener).createRequest(command);
+		}
+
+		@Test
+		void shouldCallBescheidService() {
+			var bescheidRequest = BescheidRequestTestFactory.create();
+			doReturn(bescheidRequest).when(listener).createRequest(any());
+
+			listener.doCreateBescheidBiz(command);
+
+			verify(service).createBescheid(bescheidRequest);
+		}
+
+		@Test
+		void shouldCallFileService() {
+			var bescheid = BescheidTestFactory.create();
+			when(service.createBescheid(any())).thenReturn(bescheid);
+
+			listener.doCreateBescheidBiz(command);
+
+			verify(fileService).uploadBescheidFile(bescheid);
+		}
+
+		@Test
+		void shouldReturnBescheid() {
+			var bescheid = BescheidTestFactory.create();
+			when(fileService.uploadBescheidFile(any())).thenReturn(bescheid);
+
+			var result = listener.doCreateBescheidBiz(command);
+
+			assertThat(result).isSameAs(bescheid);
+		}
+	}
+
+	@Nested
+	class TestOnSendBescheidCommand {
+
+		private final Command command = CommandTestFactory.create();
+
+		@Test
+		void shouldCallRunWithSecurityContext() {
+			listener.onSendBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).runWithSecurityContext(eq(command), any());
+		}
+
+		@Test
+		void shouldExecuteDoSendBescheid() {
+			listener.onSendBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).doSendBescheid(command);
+		}
+	}
+
+	@Nested
+	class TestDoSendBescheid {
+
+		private static final Command COMMAND = CommandTestFactory.createBuilder().relationId(AttachedItemTestFactory.ID)
+				.relationVersion(AttachedItemTestFactory.VERSION).build();
+
 		@Captor
-		private ArgumentCaptor<CommandFailedEvent> updateFailedEventCaptor;
+		private ArgumentCaptor<BescheidSentEvent> bescheidSentEventCaptor;
+
+		@Test
+		void shouldCallSendBescheid() {
+
+			listener.doSendBescheid(COMMAND);
 
-		private Command command = CommandTestFactory.createBuilder().build();
+			verify(service).sendBescheidManually(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION);
+		}
 
 		@Test
-		void shouldCallUpdateBescheidDraft() {
-			listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+		void shouldPublishEvent() {
+			listener.doSendBescheid(COMMAND);
 
-			verify(attachedItemService).updateBescheidDraft(command);
+			verify(eventPublisher).publishEvent(bescheidSentEventCaptor.capture());
+			assertThat(bescheidSentEventCaptor.getValue().getCommand()).isEqualTo(COMMAND);
+		}
+	}
+
+	@Nested
+	class TestOnSendPostfachMailCommand {
+
+		private final Command command = CommandTestFactory.create();
+
+		@Test
+		void shouldCallRunWithSecurityContext() {
+			listener.onSendPostfachMailCommand(CommandCreatedEventTestFactory.withCommand(command));
+
+			verify(listener).runWithSecurityContext(eq(command), any());
 		}
 
 		@Test
-		void shouldPublishBescheidUpdatedEvent() {
-			listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+		void shouldExecuteDoSendPostfachMail() {
+			listener.onSendPostfachMailCommand(CommandCreatedEventTestFactory.withCommand(command));
 
-			verify(eventPublisher).publishEvent(bescheidUpdatedEventCaptor.capture());
-			assertThat(bescheidUpdatedEventCaptor.getValue().getCommand()).isSameAs(command);
+			verify(listener).doSendPostfachMail(command);
 		}
+	}
+
+	@Nested
+	class TestDoSendPostfachMail {
+
+		@Captor
+		private ArgumentCaptor<BescheidSentEvent> bescheidSentEventCaptor;
 
 		@Test
-		void shouldPublishCommandFailedEvent() {
-			doThrow(new RuntimeException("ups")).when(attachedItemService).updateBescheidDraft(any());
+		void shouldCallBescheidService() {
+			var bescheidId = "bescheid-id";
+			var bescheidVersion = 1L;
+			var command = CommandTestFactory.createBuilder().relationId(bescheidId).relationVersion(bescheidVersion).build();
 
-			listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+			listener.doSendPostfachMail(command);
 
-			verify(eventPublisher).publishEvent(updateFailedEventCaptor.capture());
-			assertThat(updateFailedEventCaptor.getValue().getSource()).isEqualTo(command.getId());
+			verify(service).sendBescheidPostfachMail(bescheidId, bescheidVersion);
 		}
 
-		@Nested
-		class HandleSecurityContext {
+		@Test
+		void shouldPublishEvent() {
+			var command = CommandTestFactory.createBuilder().relationVersion(0L).build();
 
-			@Mock
-			private SecurityContext secContext;
+			listener.doSendPostfachMail(command);
 
-			@BeforeEach
-			void init() {
-				when(userService.startSecurityContext(any())).thenReturn(secContext);
-			}
+			verify(eventPublisher).publishEvent(bescheidSentEventCaptor.capture());
+			assertThat(bescheidSentEventCaptor.getValue().getCommand()).isEqualTo(command);
+		}
+	}
 
-			@Test
-			void shouldStartSecurityContext() {
-				listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+	@Nested
+	class TestRunWithSecurityContext {
 
-				verify(userService).startSecurityContext(command);
-			}
+		private final Command command = CommandTestFactory.createBuilder().build();
 
-			@Test
-			void shouldResetSecurityContext() {
-				listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+		@Mock
+		private Consumer<Command> commandExecutor;
+		@Mock
+		private SecurityContext secContext;
+		@Captor
+		private ArgumentCaptor<CommandFailedEvent> commandFailedEventCaptor;
 
-				verify(userService).resetSecurityContext(secContext);
-			}
+		@BeforeEach
+		void init() {
+			when(userService.startSecurityContext(any())).thenReturn(secContext);
+		}
 
-			@Test
-			void shouldResetSecurityContextAfterException() {
-				doThrow(new RuntimeException("ups")).when(attachedItemService).updateBescheidDraft(any());
+		@Test
+		void shouldStartSecurityContext() {
+			listener.runWithSecurityContext(command, commandExecutor);
 
-				listener.onUpdateBescheidCommand(CommandCreatedEventTestFactory.withCommand(command));
+			verify(userService).startSecurityContext(command);
+		}
 
-				verify(userService).resetSecurityContext(secContext);
-			}
+		@Test
+		void shouldExecuteCommand() {
+			listener.runWithSecurityContext(command, commandExecutor);
+
+			verify(commandExecutor).accept(command);
+		}
+
+		@Test
+		void shouldResetSecurityContext() {
+			listener.runWithSecurityContext(command, commandExecutor);
+
+			verify(userService).resetSecurityContext(secContext);
+		}
+
+		@Test
+		void shouldResetSecurityContextAfterException() {
+			doThrow(new RuntimeException("ups")).when(commandExecutor).accept(any());
+
+			listener.runWithSecurityContext(command, commandExecutor);
+
+			verify(userService).resetSecurityContext(secContext);
 		}
 
+		@Test
+		void shouldPublishCommandFailedEvent() {
+			doThrow(new RuntimeException("ups")).when(commandExecutor).accept(any());
+
+			listener.runWithSecurityContext(command, commandExecutor);
 
+			verify(eventPublisher).publishEvent(commandFailedEventCaptor.capture());
+			assertThat(commandFailedEventCaptor.getValue().getSource()).isEqualTo(command.getId());
+			assertThat(commandFailedEventCaptor.getValue().getErrorMessage()).isNotEmpty();
+		}
 	}
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java
index afbc1a5a925c54528878259074c6b64c1b78bc81..2d80f5de69e2af46c5a3d1ad6dba7229f4e2932d 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java
@@ -1,21 +1,45 @@
 package de.ozgcloud.bescheid;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Spy;
+import org.springframework.test.util.ReflectionTestUtils;
 
+import de.ozgcloud.bescheid.attacheditem.AttachedItem;
+import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
+import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory;
+import de.ozgcloud.bescheid.attributes.ClientAttributeService;
+import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.bescheid.common.callcontext.UserProfile;
+import de.ozgcloud.bescheid.nachricht.NachrichtService;
+import de.ozgcloud.bescheid.vorgang.ServiceKontoTestFactory;
 import de.ozgcloud.bescheid.vorgang.Vorgang;
+import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.bescheid.vorgang.VorgangService;
 import de.ozgcloud.bescheid.vorgang.VorgangTestFactory;
+import de.ozgcloud.command.CommandTestFactory;
+import de.ozgcloud.common.errorhandling.TechnicalException;
 
 class BescheidServiceTest {
 
+	@Spy
 	@InjectMocks
 	private BescheidService service;
 
@@ -23,12 +47,27 @@ class BescheidServiceTest {
 	private VorgangService vorgangService;
 	@Mock
 	private BescheidRemoteService remoteService;
+	@Mock
+	private AttachedItemService attachedItemService;
+	@Mock
+	private NachrichtService nachrichtService;
+	@Mock
+	private CurrentUserService currentUserService;
+	@Mock
+	private UserProfile callContextUser;
+	@Mock
+	private ClientAttributeService clientAttributeService;
+
+	@BeforeEach
+	void init() {
+		ReflectionTestUtils.setField(service, "remoteService", Optional.of(remoteService));
+	}
 
 	@Nested
 	class TestCreateBescheid {
 
-		private Vorgang vorgang = VorgangTestFactory.create();
-		private Bescheid bescheid = BescheidTestFactory.createBuilder().vorgangId(null).build();
+		private final Vorgang vorgang = VorgangTestFactory.create();
+		private final Bescheid bescheid = BescheidTestFactory.createBuilder().vorgangId(null).build();
 
 		@BeforeEach
 		void initMocks() {
@@ -60,4 +99,499 @@ class BescheidServiceTest {
 		}
 	}
 
+	@Nested
+	class TestSendBescheidManually {
+
+		private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid();
+
+		@BeforeEach
+		void init() {
+			doNothing().when(service).validateBescheidSendManually(any(), anyLong());
+			doReturn(bescheidItem).when(attachedItemService).getItem(anyString());
+		}
+
+		@Test
+		void shouldCallGetItem() {
+			sendBescheid();
+
+			verify(attachedItemService).getItem(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallValidateBescheid() {
+			sendBescheid();
+
+			verify(service).validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldCallSendBescheid() {
+			sendBescheid();
+
+			verify(service).sendBescheid(bescheidItem);
+		}
+
+		private void sendBescheid() {
+			service.sendBescheidManually(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION);
+		}
+	}
+
+	@Nested
+	class TestValidateBescheidSendManually {
+
+		@Test
+		void shouldCallValidateBescheid() {
+			var bescheidItem = AttachedItemTestFactory.createBescheid();
+
+			service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION);
+
+			verify(service).validateBescheid(bescheidItem, AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldValidate() {
+			doNothing().when(service).validateBescheid(any(), anyLong());
+			var bescheidItem = AttachedItemTestFactory.createBescheid();
+
+			assertDoesNotThrow(() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION));
+		}
+
+		@DisplayName("should decline when")
+		@ParameterizedTest(name = "sendBy = {0}")
+		@EnumSource(value = Bescheid.SendBy.class, names = "MANUAL", mode = EnumSource.Mode.EXCLUDE)
+		void shouldDeclineSendByNotManual(Bescheid.SendBy sendBy) {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().itemEntry(Bescheid.FIELD_SEND_BY, sendBy.name()).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("sendBy");
+		}
+	}
+
+	@Nested
+	class TestSendBescheidPostfachMail {
+
+		private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid();
+		@Mock
+		private AttachedItem bescheidSendItem;
+		@Captor
+		private ArgumentCaptor<VorgangId> vorgangIdCaptor;
+
+		@BeforeEach
+		void init() {
+			doNothing().when(service).validateBescheidSendPostfach(any(), anyLong());
+			doReturn(bescheidItem).when(attachedItemService).getItem(anyString());
+			lenient().when(callContextUser.getId()).thenReturn(UserId.from("user-id"));
+			lenient().when(currentUserService.getUserProfile()).thenReturn(callContextUser);
+		}
+
+		@Test
+		void shouldCallGetItem() {
+			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
+
+			sendBescheid();
+
+			verify(attachedItemService).getItem(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallValidateBescheid() {
+			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
+
+			sendBescheid();
+
+			verify(service).validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldCallVorgangService() {
+			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
+
+			sendBescheid();
+
+			verify(vorgangService).getById(vorgangIdCaptor.capture());
+			assertThat(vorgangIdCaptor.getValue()).hasToString(CommandTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldCallBuildBescheid() {
+			var serviceKonto = ServiceKontoTestFactory.create();
+			var vorgang = VorgangTestFactory.createBuilder().serviceKonto(serviceKonto).build();
+			doReturn(vorgang).when(vorgangService).getById(any());
+
+			sendBescheid();
+
+			verify(service).buildBescheid(bescheidItem, serviceKonto);
+		}
+
+		@Test
+		void shouldCallNachrichtService() {
+			var bescheid = BescheidTestFactory.create();
+			doReturn(bescheid).when(service).buildBescheid(any(), any());
+			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
+
+			sendBescheid();
+
+			verify(nachrichtService).createNachrichtDraft(bescheid);
+		}
+
+		@Test
+		void shouldCallSendBescheid() {
+			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
+
+			sendBescheid();
+
+			verify(service).sendBescheid(bescheidItem);
+		}
+
+		private void sendBescheid() {
+			service.sendBescheidPostfachMail(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION);
+		}
+	}
+
+	@Nested
+	class TestValidateBescheidSendPostfach {
+
+		@Test
+		void shouldCallValidateBescheid() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder()
+					.itemEntry(Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name())
+					.itemEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT)
+					.itemEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT).build();
+
+			service.validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION);
+
+			verify(service).validateBescheid(bescheidItem, AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldValidate() {
+			doNothing().when(service).validateBescheid(any(), anyLong());
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder()
+					.itemEntry(Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name())
+					.itemEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT)
+					.itemEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT).build();
+
+			assertDoesNotThrow(() -> service.validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION));
+		}
+
+		@DisplayName("should decline when")
+		@ParameterizedTest(name = "sendBy = {0}")
+		@EnumSource(value = Bescheid.SendBy.class, names = "NACHRICHT", mode = EnumSource.Mode.EXCLUDE)
+		void shouldDeclineSendByNotPostfach(Bescheid.SendBy sendBy) {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder()
+					.itemEntry(Bescheid.FIELD_SEND_BY, sendBy.name())
+					.itemEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT)
+					.itemEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("sendBy");
+		}
+
+		@Test
+		void shouldDeclineWhenNoSubject() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder()
+					.itemEntry(Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name())
+					.itemEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, null)
+					.itemEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("nachricht subject");
+		}
+
+		@Test
+		void shouldDeclineWhenNoText() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder()
+					.itemEntry(Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name())
+					.itemEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT)
+					.itemEntry(Bescheid.FIELD_NACHRICHT_TEXT, null).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("nachricht text");
+		}
+	}
+
+	@Nested
+	class TestValidateBescheid {
+
+		@Test
+		void shouldValidateBescheid() {
+			var bescheidItem = AttachedItemTestFactory.createBescheid();
+
+			assertDoesNotThrow(() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION));
+		}
+
+		@DisplayName("should decline when")
+		@ParameterizedTest(name = "status = {0}")
+		@EnumSource(value = Bescheid.Status.class, names = "DRAFT", mode = EnumSource.Mode.EXCLUDE)
+		void shouldDeclineNotDraft(Bescheid.Status status) {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().itemEntry(Bescheid.FIELD_STATUS, status.name()).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("has status");
+		}
+
+		@Test
+		void shouldDeclineWhenNoDocument() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().itemEntry(Bescheid.FIELD_BESCHEID_DOCUMENT, null).build();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION)).getMessage();
+			assertThat(message).contains("document");
+		}
+
+		@Test
+		void shouldDeclineWhenVersionMismatch() {
+			var bescheidItem = AttachedItemTestFactory.createBescheid();
+
+			var message = assertThrows(TechnicalException.class,
+					() -> service.validateBescheidSendManually(bescheidItem, AttachedItemTestFactory.VERSION + 1)).getMessage();
+			assertThat(message).contains("version");
+		}
+	}
+
+	@Nested
+	class TestBuildBescheid {
+
+		private static final AttachedItem BESCHEID_ITEM = AttachedItemTestFactory.createBescheid();
+		private static final Vorgang.ServiceKonto SERVICE_KONTO = ServiceKontoTestFactory.create();
+		private static final String CREATED_BY = "user-id";
+
+		@BeforeEach
+		void init() {
+			when(callContextUser.getId()).thenReturn(UserId.from(CREATED_BY));
+			when(currentUserService.getUserProfile()).thenReturn(callContextUser);
+		}
+
+		@Test
+		void shouldSetVorgangId() {
+			var result = buildBescheid();
+
+			assertThat(result.getVorgangId()).hasToString(CommandTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldCallGetBewilligt() {
+			buildBescheid();
+
+			verify(service).getBewilligt(BESCHEID_ITEM);
+		}
+
+		@Test
+		void shouldSetGenehmigt() {
+			doReturn(true).when(service).getBewilligt(any());
+
+			var result = buildBescheid();
+
+			assertThat(result.isGenehmigt()).isTrue();
+		}
+
+		@Test
+		void shouldSetBescheidFileId() {
+			var result = buildBescheid();
+
+			assertThat(result.getBescheidFileId()).hasToString(AttachedItemTestFactory.BESCHEID_DOCUMENT);
+		}
+
+		@Test
+		void shouldSetNachrichtSubject() {
+			var result = buildBescheid();
+
+			assertThat(result.getNachrichtSubject()).contains(AttachedItemTestFactory.NACHRICHT_SUBJECT);
+		}
+
+		@Test
+		void shouldSetNachrichtText() {
+			var result = buildBescheid();
+
+			assertThat(result.getNachrichtText()).contains(AttachedItemTestFactory.NACHRICHT_TEXT);
+		}
+
+		@Test
+		void shouldCallCurrentUserService() {
+			buildBescheid();
+
+			verify(currentUserService).getUserProfile();
+		}
+
+		@Test
+		void shouldSetCreatedBy() {
+			var result = buildBescheid();
+
+			assertThat(result.getCreatedBy()).hasToString(CREATED_BY);
+		}
+
+		@Test
+		void shouldSetServiceKonto() {
+			var result = buildBescheid();
+
+			assertThat(result.getServiceKonto()).usingRecursiveComparison().isEqualTo(SERVICE_KONTO);
+		}
+
+		private Bescheid buildBescheid() {
+			return service.buildBescheid(BESCHEID_ITEM, SERVICE_KONTO);
+		}
+	}
+
+	@Nested
+	class TestGetNachrichtSubject {
+
+		@Test
+		void shouldReturnSubject() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().item(Map.of(Bescheid.FIELD_NACHRICHT_SUBJECT,
+					AttachedItemTestFactory.NACHRICHT_SUBJECT)).build();
+
+			var result = service.getNachrichtSubject(bescheidItem);
+
+			assertThat(result).isEqualTo(AttachedItemTestFactory.NACHRICHT_SUBJECT);
+		}
+	}
+
+	@Nested
+	class TestGetNachrichtText {
+
+		@Test
+		void shouldReturnText() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().item(Map.of(Bescheid.FIELD_NACHRICHT_TEXT,
+					AttachedItemTestFactory.NACHRICHT_TEXT)).build();
+
+			var result = service.getNachrichtText(bescheidItem);
+
+			assertThat(result).isEqualTo(AttachedItemTestFactory.NACHRICHT_TEXT);
+		}
+	}
+
+	@Nested
+	class TestSendBescheid {
+
+		private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid();
+		@Mock
+		private AttachedItem bescheidSendItem;
+
+		@Test
+		void shouldCallBuildBescheidSend() {
+			sendBescheid();
+
+			verify(service).setBescheidSendStatus(bescheidItem);
+		}
+
+		@Test
+		void shouldCallPatch() {
+			doReturn(bescheidSendItem).when(service).setBescheidSendStatus(any());
+
+			sendBescheid();
+
+			verify(attachedItemService).patch(bescheidSendItem);
+		}
+
+		@Test
+		void shouldCallBescheiden() {
+			sendBescheid();
+
+			verify(vorgangService).bescheiden(CommandTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldCallGetItem() {
+			when(attachedItemService.getItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
+			doThrow(new TechnicalException("error")).when(vorgangService).bescheiden(anyString());
+
+			assertThrows(TechnicalException.class, this::sendBescheid);
+
+			verify(attachedItemService).getItem(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallSetBescheidDraftStatus() {
+			doThrow(new TechnicalException("error")).when(vorgangService).bescheiden(anyString());
+			var updatedBescheidUitem = AttachedItemTestFactory.createBescheid();
+			when(attachedItemService.getItem(any())).thenReturn(updatedBescheidUitem);
+
+			assertThrows(TechnicalException.class, this::sendBescheid);
+
+			verify(service).setBescheidDraftStatus(updatedBescheidUitem);
+		}
+
+		@Test
+		void shouldCallPatchWhenBescheidenFails() {
+			doReturn(bescheidSendItem).when(service).setBescheidDraftStatus(any());
+			doThrow(new TechnicalException("error")).when(vorgangService).bescheiden(anyString());
+
+			assertThrows(TechnicalException.class, this::sendBescheid);
+
+			verify(attachedItemService).patch(bescheidSendItem);
+		}
+
+		@Test
+		void shouldCallGetBewilligt() {
+			sendBescheid();
+
+			verify(service).getBewilligt(bescheidItem);
+		}
+
+		@Test
+		void shouldCallClientAttributeService() {
+			doReturn(true).when(service).getBewilligt(any());
+
+			sendBescheid();
+
+			verify(clientAttributeService).setAntragResult(CommandTestFactory.VORGANG_ID, true);
+		}
+
+		private void sendBescheid() {
+			service.sendBescheid(bescheidItem);
+		}
+	}
+
+	@Nested
+	class TestGetBewilligt {
+
+		@Test
+		void shouldReturnBewilligt() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().itemEntry(Bescheid.FIELD_BEWILLIGT, true).build();
+
+			var result = service.getBewilligt(bescheidItem);
+
+			assertThat(result).isTrue();
+		}
+
+		@Test
+		void shouldReturnDefault() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().clearItem().build();
+
+			var result = service.getBewilligt(bescheidItem);
+
+			assertThat(result).isFalse();
+		}
+	}
+
+	@Nested
+	class TestSetBescheidSendStatus {
+
+		@Test
+		void shouldSetSendStatus() {
+			var bescheidItem = AttachedItemTestFactory.createBescheid();
+
+			var result = service.setBescheidSendStatus(bescheidItem);
+
+			assertThat(result.getItem()).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SEND.name());
+		}
+	}
+
+	@Nested
+	class TestSetBescheidDraftStatus {
+
+		@Test
+		void shouldSetDraftStatus() {
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().item(Collections.emptyMap()).build();
+
+			var result = service.setBescheidDraftStatus(bescheidItem);
+
+			assertThat(result.getItem()).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name());
+		}
+	}
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestFactory.java
index e4ccee014906c47e7bf442205002675ca054cad5..a0fac1f66bc68d8fe0056ca38c34f551ac163e63 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestFactory.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestFactory.java
@@ -19,8 +19,10 @@ public class BescheidTestFactory {
 
 	public static final byte[] TEST_BESCHEID = "testbescheid".getBytes();
 	public static final File BESCHEID_FILE = TempFileUtils.writeTmpFile(TEST_BESCHEID);
+	public static final String BESCHEID_FILE_ID = "bescheid-file-id";
 
 	public static final String CONTENT_TYPE = MediaType.APPLICATION_PDF_VALUE;
+	public static final String NACHRICHT_SUBJECT = LoremIpsum.getInstance().getWords(5);
 	public static final String NACHRICHT_TEXT = LoremIpsum.getInstance().getWords(5);
 
 	public static Bescheid create() {
@@ -34,6 +36,7 @@ public class BescheidTestFactory {
 				.contentType(CONTENT_TYPE)
 				.bescheidFileName(FILE_NAME)
 				.bescheidFile(BESCHEID_FILE)
+				.nachrichtSubject(Optional.of(NACHRICHT_SUBJECT))
 				.nachrichtText(Optional.of(NACHRICHT_TEXT))
 				.genehmigt(true)
 				.size(TEST_BESCHEID.length)
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapperTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..782c98d8cc4bc4066b43d34e9a45334d724ca7d5
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemMapperTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attacheditem;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import de.ozgcloud.document.DocumentService;
+import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper;
+
+class AttachedItemMapperTest {
+
+	@InjectMocks
+	private AttachedItemMapper mapper = Mappers.getMapper(AttachedItemMapper.class);
+
+	@Mock
+	private GrpcObjectMapper grpcObjectMapper;
+
+	@BeforeEach
+	void init() {
+		when(grpcObjectMapper.mapFromGrpc(any())).thenReturn(AttachedItemTestFactory.createDocumentItem());
+	}
+
+	@Test
+	void shouldMapFromVorgangAttachedItem() {
+		var grpcVorgangAttachedItem = GrpcVorgangAttachedItemTestFactory.createBuilder().setItemName(DocumentService.DOCUMENT_ITEM_NAME)
+				.setVersion(AttachedItemTestFactory.VERSION).build();
+
+		var result = mapper.mapFromVorgangAttachedItem(grpcVorgangAttachedItem);
+
+		assertThat(result).usingRecursiveComparison().isEqualTo(AttachedItemTestFactory.createDocument());
+	}
+
+}
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java
index 3c10b5cdb070f9c0cbd60590cf805170c0491632..abb9739abd06cf487fe5e8d1919eb186f22394ea 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java
@@ -115,7 +115,7 @@ class AttachedItemServiceTest {
 		private static final String CREATED_ATTACHED_ITEM_ID = "attached-item-id";
 
 		private Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
-		private BescheidItem bescheidItem = BescheidItemTestFactory.create();
+		private final BescheidItem bescheidItem = BescheidItemTestFactory.create();
 
 		@Mock
 		private OzgCloudCommand ozgCloudCommand;
@@ -295,8 +295,8 @@ class AttachedItemServiceTest {
 		@Mock
 		private OzgCloudCommand updateAttachedItemCommand;
 
-		private Command command = CommandTestFactory.createBuilder().relationId(BescheidItemTestFactory.ID).build();
-		private BescheidItem bescheidItem = BescheidItemTestFactory.create();
+		private final Command command = CommandTestFactory.createBuilder().relationId(BescheidItemTestFactory.ID).build();
+		private final BescheidItem bescheidItem = BescheidItemTestFactory.create();
 
 		@BeforeEach
 		void init() {
@@ -365,7 +365,7 @@ class AttachedItemServiceTest {
 	@Nested
 	class TestBuildAttachedItemAsMap {
 
-		private Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
+		private final Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
 
 		@Test
 		void shouldSetVorgangId() {
@@ -405,7 +405,7 @@ class AttachedItemServiceTest {
 		@Mock
 		private OzgCloudCommand updateItemCommand;
 
-		private Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
+		private final Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
 
 		@BeforeEach
 		void init() {
@@ -499,7 +499,7 @@ class AttachedItemServiceTest {
 	@Nested
 	class TestBuildBescheidMap {
 
-		private Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
+		private final Command command = CommandTestFactory.createBuilder().bodyObject(BescheidItemTestFactory.createBescheidBody()).build();
 
 		@Test
 		void shouldSetStatus() {
@@ -626,7 +626,7 @@ class AttachedItemServiceTest {
 		@Mock
 		private OzgCloudCommand deleteItemOzgCloudCommand;
 
-		private Command command = CommandTestFactory.createBuilder().relationId(BescheidItemTestFactory.ID).build();
+		private final Command command = CommandTestFactory.createBuilder().relationId(BescheidItemTestFactory.ID).build();
 
 		@Test
 		void shouldCallGetBescheid() {
@@ -727,4 +727,171 @@ class AttachedItemServiceTest {
 		}
 
 	}
+
+	@Nested
+	class TestGetItem {
+
+		@Test
+		void shouldCallRemoteService() {
+			service.getItem(BescheidItemTestFactory.ID);
+
+			verify(service).getItem(BescheidItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldReturnValue() {
+			var expectedItem = AttachedItemTestFactory.createDocument();
+			doReturn(expectedItem).when(remoteService).getItem(any());
+
+			var result = service.getItem(BescheidItemTestFactory.ID);
+
+			assertThat(result).isEqualTo(expectedItem);
+		}
+	}
+
+	@Nested
+	class TestPatch {
+
+		@Mock
+		private OzgCloudCommand command;
+
+		@Test
+		void shouldCallBuildPatchBescheidCommand() {
+			var item = AttachedItemTestFactory.createDocument();
+
+			service.patch(item);
+
+			verify(service).buildPatchBescheidCommand(item);
+		}
+
+		@Test
+		void shouldCallCommandService() {
+			doReturn(command).when(service).buildPatchBescheidCommand(any());
+
+			service.patch(AttachedItemTestFactory.createDocument());
+
+			verify(commandService).createAndWaitUntilDone(command);
+		}
+	}
+
+	@Nested
+	class TestBuildPatchBescheidCommand {
+
+		@Test
+		void shouldCallVorgangIdMapper() {
+			service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			verify(commandMapper).toOzgCloudVorgangId(CommandTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldSetVorgangId() {
+			var expectedVorgangId = OzgCloudVorgangId.from(CommandTestFactory.VORGANG_ID);
+			when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedVorgangId);
+
+			var result = service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			assertThat(result.getVorgangId()).isEqualTo(expectedVorgangId);
+		}
+
+		@Test
+		void shouldCallRelationIdMapper() {
+			service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			verify(commandMapper).mapRelationId(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldSetRelationId() {
+			var expectedId = GenericId.from(AttachedItemTestFactory.ID);
+			when(commandMapper.mapRelationId(any())).thenReturn(expectedId);
+
+			var result = service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			assertThat(result.getRelationId()).isEqualTo(expectedId);
+		}
+
+		@Test
+		void shouldSetRelationVersion() {
+			var result = service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			assertThat(result.getRelationVersion()).isEqualTo(AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldSetOrder() {
+			var result = service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			assertThat(result.getOrder()).isEqualTo(AttachedItemService.PATCH_ATTACHED_ITEM);
+		}
+
+		@Test
+		void shouldCallBuildObjectMap() {
+			var bescheidItem = AttachedItemTestFactory.createDocument();
+
+			service.buildPatchBescheidCommand(bescheidItem);
+
+			verify(service).buildObjectMap(bescheidItem);
+		}
+
+		@Test
+		void shouldSetBodyObject() {
+			var expectedMap = Map.of("key", (Object) "value");
+			doReturn(expectedMap).when(service).buildObjectMap(any());
+
+			var result = service.buildPatchBescheidCommand(AttachedItemTestFactory.createDocument());
+
+			assertThat(result.getBodyObject()).containsAllEntriesOf(expectedMap);
+		}
+	}
+
+	@Nested
+	class TestBuildBodyObject {
+
+		@Test
+		void shouldSetId() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_ID, AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldSetClient() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_CLIENT, AttachedItemTestFactory.CLIENT);
+		}
+
+		@Test
+		void shouldSetVorgangId() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_VORGANG_ID, CommandTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldSetItemName() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_ITEM_NAME, AttachedItemService.BESCHEID_ITEM_NAME);
+		}
+
+		@Test
+		void shouldSetVersion() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_VERSION, AttachedItemTestFactory.VERSION);
+		}
+
+		@Test
+		void shouldSetItem() {
+			var result = buildObjectMap();
+
+			assertThat(result).containsEntry(AttachedItem.PROPERTY_ITEM, AttachedItemTestFactory.createBescheidItem());
+		}
+
+		private Map<String, Object> buildObjectMap() {
+			return service.buildObjectMap(AttachedItemTestFactory.createBescheid());
+		}
+	}
 }
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..71fdad4db515fc47f523b215e415d2f1d5bcf8d4
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemTestFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attacheditem;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ozgcloud.bescheid.Bescheid;
+import de.ozgcloud.bescheid.Bescheid.Status;
+import de.ozgcloud.bescheid.attacheditem.AttachedItem.AttachedItemBuilder;
+import de.ozgcloud.command.CommandTestFactory;
+import de.ozgcloud.document.Document;
+import de.ozgcloud.document.DocumentService;
+import de.ozgcloud.document.DocumentTestFactory;
+
+public class AttachedItemTestFactory {
+
+	public static final String ID = "bescheid-item-id";
+	public static final long VERSION = 10L;
+	public static final String CLIENT = "client";
+
+	public static final String BESCHEIDEN_AM = "2024-01-01";
+	public static final String BESCHEID_DOCUMENT = "bescheid-document";
+	public static final String ATTACHMENT = "attachment-id";
+	public static final Bescheid.SendBy SEND_BY = Bescheid.SendBy.MANUAL;
+	public static final String NACHRICHT_TEXT = "nachricht-text";
+	public static final String NACHRICHT_SUBJECT = "nachricht-subject";
+
+	public static AttachedItem createBescheid() {
+		return createBescheidBuilder().build();
+	}
+
+	public static AttachedItem.AttachedItemBuilder createBescheidBuilder() {
+		return createBuilder()
+				.itemName(AttachedItemService.BESCHEID_ITEM_NAME)
+				.item(createBescheidItem());
+	}
+
+	public static Map<String, Object> createBescheidItem() {
+		var item = new HashMap<String, Object>();
+		item.put(Bescheid.FIELD_STATUS, Status.DRAFT);
+		item.put(Bescheid.FIELD_BESCHIEDEN_AM, BESCHEIDEN_AM);
+		item.put(Bescheid.FIELD_BEWILLIGT, true);
+		item.put(Bescheid.FIELD_BESCHEID_DOCUMENT, BESCHEID_DOCUMENT);
+		item.put(Bescheid.FIELD_ATTACHMENTS, List.of(ATTACHMENT));
+		item.put(Bescheid.FIELD_SEND_BY, SEND_BY.name());
+		item.put(Bescheid.FIELD_NACHRICHT_TEXT, NACHRICHT_TEXT);
+		item.put(Bescheid.FIELD_NACHRICHT_SUBJECT, NACHRICHT_SUBJECT);
+		return item;
+	}
+
+	public static AttachedItem createDocument() {
+		return createDocumentBuilder().build();
+	}
+
+	public static AttachedItem.AttachedItemBuilder createDocumentBuilder() {
+		return createBuilder()
+				.itemName(DocumentService.DOCUMENT_ITEM_NAME)
+				.item(createDocumentItem());
+	}
+
+	public static HashMap<String, Object> createDocumentItem() {
+		var map = new HashMap<String, Object>();
+		map.put(Document.FIELD_DOCUMENT_TYPE, DocumentService.DOCUMENT_TYPE);
+		map.put(Document.FIELD_DOCUMENT_FILE, DocumentTestFactory.DOCUMENT_FILE);
+		map.put(Document.FIELD_NACHRICHT_TEXT, DocumentTestFactory.NACHRICHT_TEXT);
+		map.put(Document.FIELD_NACHRICHT_SUBJECT, DocumentTestFactory.NACHRICHT_SUBJECT);
+		return map;
+	}
+
+	public static AttachedItemBuilder createBuilder() {
+		return AttachedItem.builder()
+				.id(ID)
+				.version(VERSION)
+				.client(CLIENT)
+				.vorgangId(CommandTestFactory.VORGANG_ID);
+	}
+
+}
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteServiceTest.java
index 88259ab1300ec4b86712144b5c196f761c7a231a..444249e06cc391cd8b6d05304ab33f93134cb899 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/VorgangAttachedItemRemoteServiceTest.java
@@ -26,7 +26,6 @@
 package de.ozgcloud.bescheid.attacheditem;
 
 import static org.assertj.core.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Map;
@@ -51,7 +50,6 @@ import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemRequest;
 import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItemResponse;
 import de.ozgcloud.vorgang.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub;
 import io.grpc.ClientInterceptor;
-import io.grpc.StatusRuntimeException;
 
 class VorgangAttachedItemRemoteServiceTest {
 
@@ -67,6 +65,8 @@ class VorgangAttachedItemRemoteServiceTest {
 	private ClientInterceptor bescheidCallContextInterceptor;
 	@Mock
 	private BescheidItemMapper bescheidItemMapper;
+	@Mock
+	private AttachedItemMapper attachedItemMapper;
 
 	@Nested
 	class TestFindBescheidDraft {
@@ -308,15 +308,77 @@ class VorgangAttachedItemRemoteServiceTest {
 			assertThat(result).isEqualTo(expectedBescheid);
 		}
 
+		private BescheidItem getBescheid() {
+			return service.getBescheid(BescheidItemTestFactory.ID);
+		}
+	}
+
+	@Nested
+	class TestGetItem {
+
+		@Mock
+		private GrpcVorgangAttachedItemRequest grpcVorgangAttachedItemRequest;
+		@Mock
+		private GrpcVorgangAttachedItemResponse grpcVorgangAttachedItemResponse;
+		@Mock
+		private GrpcVorgangAttachedItem grpcVorgangAttachedItem;
+
+		@BeforeEach
+		void init() {
+			when(serviceStub.getById(any())).thenReturn(grpcVorgangAttachedItemResponse);
+			doReturn(serviceStub).when(service).getServiceStub();
+		}
+
 		@Test
-		void shouldThrowExceptionIfNotFound() {
-			when(serviceStub.getById(any())).thenThrow(StatusRuntimeException.class);
+		void shouldCallGetServiceStab() {
+			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
+
+			getItem();
 
-			assertThrows(StatusRuntimeException.class, this::getBescheid);
+			verify(service).getServiceStub();
 		}
 
-		private BescheidItem getBescheid() {
-			return service.getBescheid(BescheidItemTestFactory.ID);
+		@Test
+		void shouldCallBuildRequest() {
+			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
+
+			getItem();
+
+			verify(service).buildGetByIdRequest(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallGetById() {
+			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
+			doReturn(grpcVorgangAttachedItemRequest).when(service).buildGetByIdRequest(any());
+
+			getItem();
+
+			verify(serviceStub).getById(grpcVorgangAttachedItemRequest);
+		}
+
+		@Test
+		void shouldCallMapper() {
+			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
+
+			getItem();
+
+			verify(attachedItemMapper).mapFromVorgangAttachedItem(grpcVorgangAttachedItem);
+		}
+
+		@Test
+		void shouldReturnFoundBescheid() {
+			var expectedItem = AttachedItemTestFactory.createDocument();
+			when(attachedItemMapper.mapFromVorgangAttachedItem(any())).thenReturn(expectedItem);
+			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
+
+			var result = getItem();
+
+			assertThat(result).isEqualTo(expectedItem);
+		}
+
+		private AttachedItem getItem() {
+			return service.getItem(BescheidItemTestFactory.ID);
 		}
 	}
 
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfa18131b58c3ccc123ba07d4848625fcbddfdfb
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeRemoteServiceTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attributes;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor;
+import de.ozgcloud.vorgang.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcAccessPermission;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcClientAttribute;
+import de.ozgcloud.vorgang.grpc.clientAttribute.GrpcSetClientAttributeRequest;
+
+class ClientAttributeRemoteServiceTest {
+
+	private static final String VORGANG_ID = "vorgangId";
+	private static final String ATTRIBUTE_NAME = "attributeName";
+
+	@Spy
+	@InjectMocks
+	private ClientAttributeRemoteService service;
+
+	@Mock
+	private ClientAttributeServiceBlockingStub serviceBlockingStub;
+	@Mock
+	private BescheidCallContextAttachingInterceptor bescheidCallContextInterceptor;
+
+	@Nested
+	class TestSetBooleanReadOnlyClientAttribute {
+
+		@Captor
+		private ArgumentCaptor<BescheidCallContextAttachingInterceptor> interceptorCaptor;
+
+		@BeforeEach
+		void setUp() {
+			when(serviceBlockingStub.withInterceptors(any())).thenReturn(serviceBlockingStub);
+		}
+
+		@Test
+		void shouldSetInterceptors() {
+			service.setBooleanReadOnlyClientAttribute(VORGANG_ID, ATTRIBUTE_NAME, true);
+
+			verify(serviceBlockingStub).withInterceptors(interceptorCaptor.capture());
+			assertThat(interceptorCaptor.getValue()).isEqualTo(bescheidCallContextInterceptor);
+		}
+
+		@Test
+		void shouldCallBuildRequest() {
+			service.setBooleanReadOnlyClientAttribute(VORGANG_ID, ATTRIBUTE_NAME, true);
+
+			verify(service).buildRequest(VORGANG_ID, ATTRIBUTE_NAME, true);
+		}
+
+		@Test
+		void shouldCallSetMethod() {
+			var request = GrpcSetClientAttributeRequest.newBuilder().build();
+			doReturn(request).when(service).buildRequest(any(), any(), anyBoolean());
+
+			service.setBooleanReadOnlyClientAttribute(VORGANG_ID, ATTRIBUTE_NAME, true);
+
+			verify(serviceBlockingStub).set(request);
+
+		}
+	}
+
+	@Nested
+	class TestBuildRequest {
+
+		@Mock
+		private GrpcClientAttribute clientAttribute;
+
+		@Test
+		void shouldSetVorgangId() {
+			var result = buildRequest();
+
+			assertThat(result.getVorgangId()).isEqualTo(VORGANG_ID);
+		}
+
+		@Test
+		void shouldCallBuildClientAttribute() {
+			buildRequest();
+
+			verify(service).buildClientAttribute(ATTRIBUTE_NAME, true);
+		}
+
+		@Test
+		void shouldSetClientAttribute() {
+			doReturn(clientAttribute).when(service).buildClientAttribute(any(), anyBoolean());
+
+			var result = buildRequest();
+
+			assertThat(result.getAttribute()).isEqualTo(clientAttribute);
+		}
+
+		private GrpcSetClientAttributeRequest buildRequest() {
+			return service.buildRequest(VORGANG_ID, ATTRIBUTE_NAME, true);
+		}
+	}
+
+	@Nested
+	class TestBuildClientAttribute {
+
+		@Test
+		void shouldSetClientName() {
+			var result = service.buildClientAttribute(ATTRIBUTE_NAME, true);
+
+			assertThat(result.getClientName()).isEqualTo(BescheidCallContextAttachingInterceptor.BESCHEID_MANAGER_CLIENT_NAME);
+		}
+
+		@Test
+		void shouldSetAccess() {
+			var result = service.buildClientAttribute(ATTRIBUTE_NAME, true);
+
+			assertThat(result.getAccess()).isEqualTo(GrpcAccessPermission.READ_ONLY);
+		}
+
+		@Test
+		void shouldSetAttributeName() {
+			var result = service.buildClientAttribute(ATTRIBUTE_NAME, true);
+
+			assertThat(result.getAttributeName()).isEqualTo(ATTRIBUTE_NAME);
+		}
+
+		@Test
+		void shouldSetValue() {
+			var result = service.buildClientAttribute(ATTRIBUTE_NAME, true);
+
+			assertThat(result.getValue().getBoolValue()).isTrue();
+		}
+	}
+}
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..03d31e54d9df4e505ac172fff010acf8f94f996a
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attributes/ClientAttributeServiceTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid.attributes;
+
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+class ClientAttributeServiceTest {
+
+	@InjectMocks
+	private ClientAttributeService service;
+
+	@Mock
+	private ClientAttributeRemoteService remoteService;
+
+	@Test
+	void shouldCallRemoteService() {
+		var vorgangId = "vorgangId";
+
+		service.setAntragResult(vorgangId, true);
+
+		verify(remoteService).setBooleanReadOnlyClientAttribute(vorgangId, ClientAttributeService.ATTRIBUTE_NAME_ANTRAG_BEWILLIGT, true);
+	}
+}
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java
index fb2bd022762adfa8402df8fc8d1964469e68b521..84ded81a81d00efedee18241e553e943e68cb919 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java
@@ -28,7 +28,7 @@ class NachrichtServiceTest {
 	@Nested
 	class TestCreateNachrichtDraft {
 
-		private Nachricht nachricht = NachrichtTestFactory.create();
+		private final Nachricht nachricht = NachrichtTestFactory.create();
 
 		@Test
 		void shouldCallRemoteService() {
@@ -58,6 +58,15 @@ class NachrichtServiceTest {
 			void shouldSetSubject() {
 				var nachricht = service.buildNachricht(BescheidTestFactory.create()).get();
 
+				assertThat(nachricht.getSubject()).isEqualTo(BescheidTestFactory.NACHRICHT_SUBJECT);
+			}
+
+			@Test
+			void shouldSetDefaultSubject() {
+				var bescheid = BescheidTestFactory.createBuilder().nachrichtSubject(Optional.empty()).build();
+
+				var nachricht = service.buildNachricht(bescheid).get();
+
 				assertThat(nachricht.getSubject()).isEqualTo(NachrichtService.SUBJECT);
 			}
 
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java
index 6bbf4083b967a58576187f18ff047699cc6b8f24..3126ecfe5b5f98c41749c406cd426253a65b053e 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java
@@ -1,21 +1,33 @@
 package de.ozgcloud.bescheid.smartdocuments;
 
+import static org.assertj.core.api.Assertions.*;
+
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.test.context.ActiveProfiles;
 
-import de.ozgcloud.common.test.ITCase;
+import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
+import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
 import de.ozgcloud.bescheid.BescheidRequestTestFactory;
+import de.ozgcloud.bescheid.BescheidTestApplication;
 import de.ozgcloud.bescheid.vorgang.VorgangTestFactory;
+import de.ozgcloud.common.test.ITCase;
 
 @Disabled
+@SpringBootTest(classes = BescheidTestApplication.class)
 @ITCase
 @ActiveProfiles({ "itcase", "local" })
 class SmartDocumentsBescheidRemoteServiceITCase {
 
 	@Autowired
 	private SmartDocumentsBescheidRemoteService remoteService;
+	@MockBean
+	private OzgCloudCommandService ozgCloudCommandService;
+	@MockBean
+	private CommandMapper commandMapper;
 
 	@Test
 	void createBescheid() {
@@ -23,6 +35,7 @@ class SmartDocumentsBescheidRemoteServiceITCase {
 
 		System.out.println(bescheid.getBescheidFileName());
 		System.out.println(bescheid.getBescheidFile().getAbsolutePath());
+		assertThat(bescheid.getBescheidFileName()).isNotEmpty();
 	}
 
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java
index 24d0f5ec2574b1ada388baeb44be8a8d3dfb827f..a19b60778f59f8a9fd72d3f2ce80706692a7b283 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java
@@ -37,7 +37,7 @@ class SmartDocumentsBescheidRemoteServiceTest {
 		void shouldFillBescheid() {
 			var bescheid = service.buildBescheid(BescheidRequestTestFactory.create(), SmartDocumentsResponseTestFactory.create());
 
-			assertThat(bescheid).usingRecursiveComparison().ignoringFields("serviceKonto")
+			assertThat(bescheid).usingRecursiveComparison().ignoringFields("serviceKonto", "nachrichtSubject")
 					.isEqualTo(BescheidTestFactory.createBuilder().nachrichtText(Optional.empty()).build());
 		}
 	}
@@ -56,7 +56,7 @@ class SmartDocumentsBescheidRemoteServiceTest {
 
 	@Nested
 	class TestGetNachrichtText {
-		private File xmlFile = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer.xml"));
+		private final File xmlFile = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer.xml"));
 
 		@Test
 		void shouldCallExtractText() {
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/BescheidVorgangMapperTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/BescheidVorgangMapperTest.java
index d430ba2760da66241a263864f147a6325366a699..6f64a48933ed0351295ef3415111f149ca3f6c7d 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/BescheidVorgangMapperTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/BescheidVorgangMapperTest.java
@@ -27,5 +27,12 @@ class BescheidVorgangMapperTest {
 
 			assertThat(result.getId()).isEqualTo(VorgangTestFactory.ID);
 		}
+
+		@Test
+		void shouldMapVersion() {
+			var result = mapper.mapVorgang(GrpcVorgangWithEingangTestFactory.create());
+
+			assertThat(result.getVersion()).isEqualTo(VorgangTestFactory.VERSION);
+		}
 	}
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/GrpcVorgangWithEingangTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/GrpcVorgangWithEingangTestFactory.java
index bad828ffad1f7e9b38b11fd16b42657ffb513848..63c8a880f63c6d9ceffc9f2a8a5028cce949ed34 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/GrpcVorgangWithEingangTestFactory.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/GrpcVorgangWithEingangTestFactory.java
@@ -14,6 +14,7 @@ class GrpcVorgangWithEingangTestFactory {
 	static GrpcVorgangWithEingang.Builder createBuilder() {
 		return GrpcVorgangWithEingang.newBuilder()
 				.setId(ID.toString())
+				.setVersion(VERSION)
 				.setEingang(GrpcEingangTestFactory.create());
 	}
 
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java
index 375bd58887096ea7f101b9d07a0c1e1579ce75b1..424a4330012bc1fd605dede82c56eac40c435f67 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java
@@ -9,19 +9,31 @@ import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.ozgcloud.apilib.common.command.OzgCloudCommand;
+import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
+import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
+import de.ozgcloud.apilib.common.datatypes.GenericId;
+import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId;
 
 class VorgangServiceTest {
 
+	@Spy
 	@InjectMocks
 	private VorgangService service;
 
 	@Mock
 	private VorgangRemoteService remoteService;
+	@Mock
+	private OzgCloudCommandService commandService;
+	@Mock
+	private CommandMapper commandMapper;
 
 	@Nested
 	class TestGetById {
 
-		private Vorgang vorgang = VorgangTestFactory.create();
+		private final Vorgang vorgang = VorgangTestFactory.create();
 
 		@BeforeEach
 		void init() {
@@ -43,4 +55,98 @@ class VorgangServiceTest {
 		}
 	}
 
+	@Nested
+	class TestBescheiden {
+
+		private final static Vorgang VORGANG = VorgangTestFactory.create();
+
+		@Mock
+		private OzgCloudCommand ozgCloudCommand;
+
+		@BeforeEach
+		void init() {
+			doReturn(VORGANG).when(service).getById(any(VorgangId.class));
+		}
+
+		@Test
+		void shouldCallGetBeyId() {
+			service.bescheiden(VorgangTestFactory.ID.toString());
+
+			verify(service).getById(VorgangTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallBuildBescheidenCommand() {
+			service.bescheiden(VorgangTestFactory.ID.toString());
+
+			verify(service).buildBescheidenCommand(VORGANG);
+		}
+
+		@Test
+		void shouldCallCommandService() {
+			doReturn(ozgCloudCommand).when(service).buildBescheidenCommand(any());
+
+			service.bescheiden(VorgangTestFactory.ID.toString());
+
+			verify(commandService).createAndWaitUntilDone(ozgCloudCommand);
+		}
+	}
+
+	@Nested
+	class TestBuildBescheidenCommand {
+
+		private final static Vorgang VORGANG = VorgangTestFactory.create();
+
+		@Test
+		void shouldCallCommandMapperToOzgCloudVorgangId() {
+			service.buildBescheidenCommand(VORGANG);
+
+			verify(commandMapper).toOzgCloudVorgangId(VorgangTestFactory.ID.toString());
+		}
+
+		@Test
+		void shouldSetVorgangId() {
+			var expectedId = OzgCloudVorgangId.from(VorgangTestFactory.ID.toString());
+			when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedId);
+
+			var result = service.buildBescheidenCommand(VORGANG);
+
+			assertThat(result.getVorgangId()).isEqualTo(expectedId);
+		}
+
+		@Test
+		void shouldCallCommandMapperMapRelationId() {
+			service.buildBescheidenCommand(VORGANG);
+
+			verify(commandMapper).mapRelationId(VorgangTestFactory.ID.toString());
+		}
+
+		@Test
+		void shouldSetRelationId() {
+			var expectedId = GenericId.from(VorgangTestFactory.ID.toString());
+			when(commandMapper.mapRelationId(any())).thenReturn(expectedId);
+
+			var result = service.buildBescheidenCommand(VORGANG);
+
+			assertThat(result.getRelationId()).isEqualTo(expectedId);
+		}
+
+		@Test
+		void shouldSetRelationVersion() {
+			var expectedVersion = VORGANG.getVersion();
+
+			var result = service.buildBescheidenCommand(VORGANG);
+
+			assertThat(result.getRelationVersion()).isEqualTo(expectedVersion);
+		}
+
+		@Test
+		void shouldSetOrder() {
+			var expectedOrder = VorgangService.VORGANG_BESCHEIDEN;
+
+			var result = service.buildBescheidenCommand(VORGANG);
+
+			assertThat(result.getOrder()).isEqualTo(expectedOrder);
+		}
+	}
 }
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangTestFactory.java
index d6c02552c3b866edb6e8de5d2c20439d09815d01..62009fca650eef4534876936e5ec328f0ddd593c 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangTestFactory.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangTestFactory.java
@@ -5,6 +5,7 @@ import java.util.UUID;
 public class VorgangTestFactory {
 
 	public static final VorgangId ID = VorgangId.from(UUID.randomUUID().toString());
+	public static final long VERSION = 1L;
 
 	public static Vorgang create() {
 		return createBuilder().build();
@@ -13,6 +14,7 @@ public class VorgangTestFactory {
 	public static Vorgang.VorgangBuilder createBuilder() {
 		return Vorgang.builder()
 				.id(ID)
+				.version(VERSION)
 				.serviceKonto(ServiceKontoTestFactory.create())
 				.vorgangName("KFAS_LIVE_KI_10_Haltverbot_befristet")
 				.vorgangNummer("ABC-123-XY")
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentGrpcServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentGrpcServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2157ed90a4161e3e67bfd1dcc30f0010c5ca390
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentGrpcServiceTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import io.grpc.stub.StreamObserver;
+
+class DocumentGrpcServiceTest {
+
+	@Spy
+	@InjectMocks
+	private DocumentGrpcService service;
+
+	@Mock
+	private DocumentService documentService;
+	@Mock
+	private DocumentMapper documentMapper;
+
+	@Nested
+	class TestGetDocument {
+
+		private final static GrpcGetDocumentRequest REQUEST = GrpcGetDocumentRequest.newBuilder().setId(DocumentTestFactory.ID).build();
+
+		@Mock
+		private GrpcGetDocumentResponse response;
+		@Mock
+		private StreamObserver<GrpcGetDocumentResponse> responseObserver;
+
+		@BeforeEach
+		void init() {
+			doReturn(response).when(service).buildGetDocumentResponse(any());
+		}
+
+		@Test
+		void shouldCallDocumentService() {
+			service.getDocument(REQUEST, responseObserver);
+
+			verify(documentService).getDocument(DocumentTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallBuildResponse() {
+			service.getDocument(REQUEST, responseObserver);
+
+			verify(service).buildGetDocumentResponse(any());
+		}
+
+		@Test
+		void shouldCallOnNext() {
+			service.getDocument(REQUEST, responseObserver);
+
+			verify(responseObserver).onNext(response);
+		}
+
+		@Test
+		void shouldCallOnCompleted() {
+			service.getDocument(REQUEST, responseObserver);
+
+			verify(responseObserver).onCompleted();
+		}
+	}
+
+	@Nested
+	class TestBuildGetDocumentResponse {
+
+		@Mock
+		private GrpcDocument grpcDocument;
+
+		@BeforeEach
+		void init() {
+			when(documentMapper.toGrpcDocument(any())).thenReturn(grpcDocument);
+		}
+
+		@Test
+		void shouldCallDocumentMapper() {
+			var document = DocumentTestFactory.create();
+
+			service.buildGetDocumentResponse(document);
+
+			verify(documentMapper).toGrpcDocument(document);
+		}
+
+		@Test
+		void shouldSetDocument() {
+			var result = service.buildGetDocumentResponse(DocumentTestFactory.create());
+
+			assertThat(result.getDocument()).isEqualTo(grpcDocument);
+		}
+	}
+}
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentMapperTest.java b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ba02e1e11c796f19fc771e9ed29000f911690cd
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentMapperTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+
+import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory;
+
+class DocumentMapperTest {
+
+	private DocumentMapper mapper = Mappers.getMapper(DocumentMapper.class);
+
+	@DisplayName("To grpc document")
+	@Nested
+	class TestToGrpcDocument {
+
+		@Test
+		void shouldMapDocument() {
+			var result = mapper.toGrpcDocument(DocumentTestFactory.create());
+
+			assertThat(result).usingRecursiveComparison().isEqualTo(GrpcDocumentTestFactory.create());
+		}
+
+		@Test
+		void shouldMapEmptyDocument() {
+			var result = mapper.toGrpcDocument(Document.builder().build());
+
+			assertThat(result).isEqualTo(GrpcDocument.newBuilder().build());
+		}
+	}
+
+	@DisplayName("From attached item")
+	@Nested
+	class TestFromAttachedItem {
+
+		@Test
+		void shouldMapId() {
+			var document = AttachedItemTestFactory.createDocument();
+
+			var result = mapper.fromAttachedItem(document);
+
+			assertThat(result.getId()).isEqualTo(AttachedItemTestFactory.ID);
+		}
+
+		@Test
+		void shouldMapType() {
+			var document = AttachedItemTestFactory.createDocument();
+
+			var result = mapper.fromAttachedItem(document);
+
+			assertThat(result.getType()).isEqualTo(DocumentService.DOCUMENT_TYPE);
+		}
+
+		@Test
+		void shouldMapFileId() {
+			var document = AttachedItemTestFactory.createDocument();
+
+			var result = mapper.fromAttachedItem(document);
+
+			assertThat(result.getFileId()).isEqualTo(DocumentTestFactory.DOCUMENT_FILE);
+		}
+
+		@Test
+		void shouldMapNachrichtText() {
+			var document = AttachedItemTestFactory.createDocument();
+
+			var result = mapper.fromAttachedItem(document);
+
+			assertThat(result.getNachrichtText()).isEqualTo(DocumentTestFactory.NACHRICHT_TEXT);
+		}
+
+		@Test
+		void shouldMapNachrichtSubject() {
+			var document = AttachedItemTestFactory.createDocument();
+
+			var result = mapper.fromAttachedItem(document);
+
+			assertThat(result.getNachrichtSubject()).isEqualTo(DocumentTestFactory.NACHRICHT_SUBJECT);
+		}
+	}
+}
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java
index 9ade6417382b7da3c7968fb1b994f2c568fd51eb..a9b9669ca351e63c42e259ec75d193b24c98355a 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java
@@ -28,7 +28,9 @@ import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Map;
+import java.util.Optional;
 
+import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -42,12 +44,16 @@ import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
 import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
 import de.ozgcloud.apilib.common.datatypes.GenericId;
 import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId;
+import de.ozgcloud.bescheid.Bescheid;
 import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor;
+import de.ozgcloud.bescheid.BescheidTestFactory;
 import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
+import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory;
 import de.ozgcloud.bescheid.attacheditem.BescheidItem;
 import de.ozgcloud.bescheid.attacheditem.BescheidItemTestFactory;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandTestFactory;
+import de.ozgcloud.common.binaryfile.FileId;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 
 class DocumentServiceTest {
@@ -65,10 +71,10 @@ class DocumentServiceTest {
 	private CommandMapper commandMapper;
 	@Mock
 	private AttachedItemService attachedItemService;
+	@Mock
+	private DocumentMapper documentMapper;
 
-	private Command command = CommandTestFactory.createBuilder()
-			.relationId(RELATION_ID)
-			.bodyObject(Map.of(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID)).build();
+	private Command command = CommandTestFactory.createBuilder().relationId(RELATION_ID).build();
 
 	@Nested
 	class TestCreateBescheidDocument {
@@ -78,42 +84,112 @@ class DocumentServiceTest {
 		@Mock
 		private OzgCloudCommand ozgCommand;
 
-		@BeforeEach
-		void init() {
-			when(commandService.createAndWaitUntilDone(any())).thenReturn(ozgCommand);
-			doNothing().when(service).validateBescheidItem(any());
-		}
+		@Nested
+		class TestWithCommand {
 
-		@Test
-		void shouldCallValidateBescheidItem() {
-			service.createBescheidDocument(command);
+			private Command command = CommandTestFactory.createBuilder().relationId(RELATION_ID)
+					.bodyObject(Map.of(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID)).build();
 
-			verify(service).validateBescheidItem(RELATION_ID);
-		}
+			@BeforeEach
+			void init() {
+				doReturn(CREATED_ATTACHED_ITEM_ID).when(service).createBescheidDocument(any(), anyMap());
+			}
 
-		@Test
-		void shouldCallBuildOzgCommand() {
-			service.createBescheidDocument(command);
+			@Test
+			void shouldCallBuildItemMap() {
+				service.createBescheidDocument(command);
+
+				verify(service).buildItemMap(command);
+			}
+
+			@Test
+			void shouldCallCreateBescheidDocument() {
+				var itemMap = Map.of("key", (Object) "value");
+				doReturn(itemMap).when(service).buildItemMap(any(Command.class));
+
+				service.createBescheidDocument(command);
+
+				verify(service).createBescheidDocument(command, itemMap);
+			}
 
-			verify(service).buildCreateDocumentOzgCommand(command);
+			@Test
+			void shouldReturnDocumentId() {
+				String result = service.createBescheidDocument(command);
+
+				assertThat(result).isEqualTo(CREATED_ATTACHED_ITEM_ID);
+			}
 		}
 
-		@Test
-		void shouldCallCommandService() {
-			doReturn(ozgCommand).when(service).buildCreateDocumentOzgCommand(any());
+		@Nested
+		class TestWithBescheid {
+
+			private Bescheid bescheid = BescheidTestFactory.create().withBescheidFileId(FileId.from(BescheidTestFactory.BESCHEID_FILE_ID));
+
+			@BeforeEach
+			void init() {
+				doReturn(CREATED_ATTACHED_ITEM_ID).when(service).createBescheidDocument(any(), anyMap());
+			}
+
+			@Test
+			void shouldCallBuildItemMap() {
+				service.createBescheidDocument(command, bescheid);
+
+				verify(service).buildItemMap(bescheid);
+			}
+
+			@Test
+			void shouldCallCreateBescheidDocument() {
+				var itemMap = Map.of("key", (Object) "value");
+				doReturn(itemMap).when(service).buildItemMap(any(Bescheid.class));
+
+				service.createBescheidDocument(command, bescheid);
 
-			service.createBescheidDocument(command);
+				verify(service).createBescheidDocument(command, itemMap);
+			}
 
-			verify(commandService).createAndWaitUntilDone(ozgCommand);
+			@Test
+			void shouldReturnDocumentId() {
+				String result = service.createBescheidDocument(command, bescheid);
+
+				assertThat(result).isEqualTo(CREATED_ATTACHED_ITEM_ID);
+			}
 		}
 
-		@Test
-		void shouldReturnDocumentId() {
-			when(ozgCommand.getCreatedResource()).thenReturn(CREATED_ATTACHED_ITEM_ID);
+		@Nested
+		class TestWithCommandAndItemMap {
+
+			private static final Map<String, Object> BODY_OBJECT = Map.of(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID);
+
+			@BeforeEach
+			void init() {
+				when(commandService.createAndWaitUntilDone(any())).thenReturn(ozgCommand);
+				doNothing().when(service).validateBescheidItem(any());
+			}
+
+			@Test
+			void shouldCallValidateBescheidItem() {
+				service.createBescheidDocument(command, BODY_OBJECT);
+
+				verify(service).validateBescheidItem(RELATION_ID);
+			}
+
+			@Test
+			void shouldCallCommandService() {
+				doReturn(ozgCommand).when(service).buildCreateDocumentOzgCommand(any(), anyMap());
+
+				service.createBescheidDocument(command, BODY_OBJECT);
+
+				verify(commandService).createAndWaitUntilDone(ozgCommand);
+			}
 
-			String documentId = service.createBescheidDocument(command);
+			@Test
+			void shouldReturnDocumentId() {
+				when(ozgCommand.getCreatedResource()).thenReturn(CREATED_ATTACHED_ITEM_ID);
 
-			assertThat(documentId).isEqualTo(CREATED_ATTACHED_ITEM_ID);
+				String documentId = service.createBescheidDocument(command, BODY_OBJECT);
+
+				assertThat(documentId).isEqualTo(CREATED_ATTACHED_ITEM_ID);
+			}
 		}
 	}
 
@@ -150,6 +226,7 @@ class DocumentServiceTest {
 
 			assertDoesNotThrow(this::validateBescheidItem);
 		}
+
 		void validateBescheidItem() {
 			service.validateBescheidItem(BescheidItemTestFactory.ID);
 		}
@@ -158,16 +235,18 @@ class DocumentServiceTest {
 	@Nested
 	class TestBuildCreateDocumentOzgCommand {
 
+		private static final Map<String, Object> BODY_OBJECT = Map.of(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID);
+
 		@Test
 		void shouldSetOrder() {
-			var ozgCommand = service.buildCreateDocumentOzgCommand(command);
+			var ozgCommand = service.buildCreateDocumentOzgCommand(command, BODY_OBJECT);
 
 			assertThat(ozgCommand.getOrder()).isEqualTo(DocumentService.CREATE_ATTACHED_ITEM_ORDER);
 		}
 
 		@Test
 		void shouldCallVorgangIdMapper() {
-			service.buildCreateDocumentOzgCommand(command);
+			service.buildCreateDocumentOzgCommand(command, BODY_OBJECT);
 
 			verify(commandMapper).toOzgCloudVorgangId(CommandTestFactory.VORGANG_ID);
 		}
@@ -177,14 +256,14 @@ class DocumentServiceTest {
 			var expectedVorgangId = OzgCloudVorgangId.from("vorgang-id");
 			when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedVorgangId);
 
-			var ozgCommand = service.buildCreateDocumentOzgCommand(command);
+			var ozgCommand = buildCreateDocumentOzgCommand();
 
 			assertThat(ozgCommand.getVorgangId()).isEqualTo(expectedVorgangId);
 		}
 
 		@Test
 		void shouldCallRelationIdMapper() {
-			service.buildCreateDocumentOzgCommand(command);
+			buildCreateDocumentOzgCommand();
 
 			verify(commandMapper).mapRelationId(CommandTestFactory.VORGANG_ID);
 		}
@@ -194,32 +273,28 @@ class DocumentServiceTest {
 			var expectedRelationId = GenericId.from("relation-id");
 			when(commandMapper.mapRelationId(any())).thenReturn(expectedRelationId);
 
-			var ozgCommand = service.buildCreateDocumentOzgCommand(command);
+			var ozgCommand = buildCreateDocumentOzgCommand();
 
 			assertThat(ozgCommand.getRelationId()).isEqualTo(expectedRelationId);
 		}
 
 		@Test
-		void shouldCallBuildAttachedItem() {
-			service.buildCreateDocumentOzgCommand(command);
+		void shouldSetBodyObject() {
+			var ozgCommand = buildCreateDocumentOzgCommand();
 
-			verify(service).buildAttachedItem(command);
+			assertThat(ozgCommand.getBodyObject()).isEqualTo(BODY_OBJECT);
 		}
 
-		@Test
-		void shouldSetAttachedItem() {
-			var expectedAttachedItem = Map.of("key", (Object) "value");
-			doReturn(expectedAttachedItem).when(service).buildAttachedItem(any());
-
-			var ozgCommand = service.buildCreateDocumentOzgCommand(command);
-
-			assertThat(ozgCommand.getBodyObject()).isEqualTo(expectedAttachedItem);
+		private OzgCloudCommand buildCreateDocumentOzgCommand() {
+			return service.buildCreateDocumentOzgCommand(command, BODY_OBJECT);
 		}
 	}
 
 	@Nested
 	class TestBuildAttachedItem {
 
+		private static final Map<String, Object> ITEM_MAP = Map.of("key", (Object) "value");
+
 		@Test
 		void shouldSetVorgangId() {
 			var attachedItem = buildAttachedItem();
@@ -243,54 +318,130 @@ class DocumentServiceTest {
 		}
 
 		@Test
-		void shouldCallBuildDocumentMap() {
-			buildAttachedItem();
+		void shouldSetItem() {
+			var attachedItem = buildAttachedItem();
 
-			verify(service).buildItemMap(command);
+			assertThat(attachedItem).containsEntry(BescheidItem.PROPERTY_ITEM, ITEM_MAP);
 		}
 
-		@Test
-		void shouldSetDocument() {
-			var expectedDocument = Map.of("key", (Object) "value");
-			doReturn(expectedDocument).when(service).buildItemMap(any());
+		private Map<String, Object> buildAttachedItem() {
+			return service.buildAttachedItem(command, ITEM_MAP);
+		}
+	}
 
-			var attachedItem = buildAttachedItem();
+	@Nested
+	class TestBuildItemMap {
+
+		@Nested
+		class TestBuildFromCommand {
+
+			private Command command = CommandTestFactory.createBuilder().relationId(RELATION_ID)
+					.bodyObject(Map.of(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID)).build();
 
-			assertThat(attachedItem).containsEntry(BescheidItem.PROPERTY_ITEM, expectedDocument);
+			@Test
+			void shouldDetDocumentType() {
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_TYPE, DocumentService.DOCUMENT_TYPE);
+			}
+
+			@Test
+			void shouldSetDocumentFile() {
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID);
+			}
+
+			@Test
+			void shouldThrowExceptionIfDocumentFileIsMissing() {
+				command = CommandTestFactory.createBuilder().bodyObject(Map.of()).build();
+
+				assertThrows(TechnicalException.class, () -> service.buildItemMap(command));
+			}
+
+			private Map<String, Object> buildItemMap() {
+				return service.buildItemMap(command);
+			}
 		}
 
-		private Map<String, Object> buildAttachedItem() {
-			return service.buildAttachedItem(command);
+		@Nested
+		class TestBuildFromBescheid {
+
+			private Bescheid bescheid = BescheidTestFactory.create().withBescheidFileId(FileId.from(BescheidTestFactory.BESCHEID_FILE_ID));
+
+			@Test
+			void shouldDetDocumentType() {
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_TYPE, DocumentService.DOCUMENT_TYPE);
+			}
+
+			@Test
+			void shouldSetDocumentFile() {
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_FILE, BescheidTestFactory.BESCHEID_FILE_ID);
+			}
+
+			@Test
+			void shouldSetNachrichtText() {
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_NACHRICHT_TEXT, BescheidTestFactory.NACHRICHT_TEXT);
+			}
+
+			@Test
+			void shouldSetEmptyNachrichtText() {
+				bescheid = BescheidTestFactory.createBuilder().bescheidFileId(FileId.from(BescheidTestFactory.BESCHEID_FILE_ID))
+						.nachrichtText(Optional.empty()).build();
+
+				var itemMap = buildItemMap();
+
+				assertThat(itemMap).containsEntry(DocumentService.FIELD_NACHRICHT_TEXT, StringUtils.EMPTY);
+			}
+
+			@Test
+			void shouldThrowExceptionIfFileIdMissing() {
+				bescheid = BescheidTestFactory.create();
+
+				assertThrows(TechnicalException.class, () -> service.buildItemMap(bescheid));
+
+			}
+
+			private Map<String, Object> buildItemMap() {
+				return service.buildItemMap(bescheid);
+			}
 		}
 	}
 
 	@Nested
-	class TestBuildItemMap {
+	class TestGetDocument {
 
 		@Test
-		void shouldDetDocumentType() {
-			var itemMap = buildItemMap();
+		void shouldCallAttachedItemService() {
+			service.getDocument(AttachedItemTestFactory.ID);
 
-			assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_TYPE, DocumentService.DOCUMENT_TYPE);
+			verify(attachedItemService).getItem(AttachedItemTestFactory.ID);
 		}
 
 		@Test
-		void shouldSetDocumentFile() {
-			var itemMap = buildItemMap();
+		void shouldCallDocumentMapper() {
+			var expectedItem  = AttachedItemTestFactory.createDocument();
+			when(attachedItemService.getItem(any())).thenReturn(expectedItem);
+
+			service.getDocument(AttachedItemTestFactory.ID);
 
-			assertThat(itemMap).containsEntry(DocumentService.FIELD_DOCUMENT_FILE, FILE_ID);
+			verify(documentMapper).fromAttachedItem(expectedItem);
 		}
 
 		@Test
-		void shouldThrowExceptionIfDocumentFileIsMissing() {
-			command = CommandTestFactory.createBuilder().bodyObject(Map.of()).build();
+		void shouldReturnDocument() {
+			var expectedDocument = DocumentTestFactory.create();
+			when(documentMapper.fromAttachedItem(any())).thenReturn(expectedDocument);
 
-			assertThrows(TechnicalException.class, () -> service.buildItemMap(command));
-		}
+			Document document = service.getDocument(AttachedItemTestFactory.ID);
 
-		private Map<String, Object> buildItemMap() {
-			return service.buildItemMap(command);
+			assertThat(document).isEqualTo(expectedDocument);
 		}
 	}
-
 }
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..0484d6f5492a0b4fe72bbd87b6980797c9bb6110
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentTestFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import de.ozgcloud.document.Document.DocumentBuilder;
+
+public class DocumentTestFactory {
+
+	public static final String ID = "documentId";
+	public static final String TYPE = "type";
+	public static final String DOCUMENT_FILE = "file-id";
+	public static final String NACHRICHT_SUBJECT = "subject";
+	public static final String NACHRICHT_TEXT = "text";
+
+	public static Document create() {
+		return createBuilder().build();
+	}
+
+	public static DocumentBuilder createBuilder() {
+		return Document.builder()
+				.id(ID)
+				.type(TYPE)
+				.fileId(DOCUMENT_FILE)
+				.nachrichtSubject(NACHRICHT_SUBJECT)
+				.nachrichtText(NACHRICHT_TEXT);
+	}
+}
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/document/GrpcDocumentTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/document/GrpcDocumentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c56530f7b13a986c3cd6fd0914bfc9201d152b7
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/GrpcDocumentTestFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+public class GrpcDocumentTestFactory {
+
+	public static GrpcDocument create() {
+		return createBuilder().build();
+	}
+
+	public static GrpcDocument.Builder createBuilder() {
+		return GrpcDocument.newBuilder()
+				.setId(DocumentTestFactory.ID)
+				.setType(DocumentTestFactory.TYPE)
+				.setFileId(DocumentTestFactory.DOCUMENT_FILE)
+				.setNachrichtSubject(DocumentTestFactory.NACHRICHT_SUBJECT)
+				.setNachrichtText(DocumentTestFactory.NACHRICHT_TEXT);
+	}
+}
diff --git a/nachrichten-bayernid-proxy/src/main/helm/templates/network_policy.yaml b/nachrichten-bayernid-proxy/src/main/helm/templates/network_policy.yaml
index e0effc809597979a1cdb01c636f868c63574f617..95cd70f64a419cc1e585a2f977f48d45aae0eaee 100644
--- a/nachrichten-bayernid-proxy/src/main/helm/templates/network_policy.yaml
+++ b/nachrichten-bayernid-proxy/src/main/helm/templates/network_policy.yaml
@@ -44,6 +44,9 @@ spec:
           component: vorgang-manager
 {{- with (.Values.networkPolicy).additionalIngressConfig }}
 {{ toYaml . | indent 2 }}
+{{- end }}
+{{- with (.Values.networkPolicy).additionalIngressConfigNamespace }}
+{{ toYaml . | indent 2 }}
 {{- end }}
   egress:
   - to:
@@ -62,5 +65,8 @@ spec:
 {{- with (.Values.networkPolicy).additionalEgressConfig }}
 {{ toYaml . | indent 2 }}
 {{- end }}
+{{- with (.Values.networkPolicy).additionalEgressConfigNamespace }}
+{{ toYaml . | indent 2 }}
+{{- end }}
 
 {{- end }}
\ No newline at end of file
diff --git a/nachrichten-bayernid-proxy/src/test/helm/network_policy_test.yaml b/nachrichten-bayernid-proxy/src/test/helm/network_policy_test.yaml
index 7382ad0947be9db238ff1b5d7f9de08439716165..6942329e62a7ca2a0544cc56d6f3d6d76e9b2120 100644
--- a/nachrichten-bayernid-proxy/src/test/helm/network_policy_test.yaml
+++ b/nachrichten-bayernid-proxy/src/test/helm/network_policy_test.yaml
@@ -91,16 +91,15 @@ tests:
                 - port: 5353
                   protocol: TCP
 
-  - it: add ingress rule by values
+  - it: should add additionalIngressConfig
     set:
       networkPolicy:
-        ssoPublicIp: 51.89.117.53/32
-        dnsServerNamespace: test-namespace-dns
+        dnsServerNamespace: test-dns-namespace
         additionalIngressConfig:
         - from:
           - podSelector: 
               matchLabels:
-                component: client2
+                additionalIngressConfig: yes
     asserts:
       - contains:
           path: spec.ingress
@@ -108,41 +107,62 @@ tests:
             from:
             - podSelector: 
                 matchLabels:
-                  component: client2
+                  additionalIngressConfig: yes
 
-  - it: add egress rules by values
+  - it: should add additionalEgressConfig
     set:
-      networkPolicy:
+      networkPolicy: 
+        dnsServerNamespace: test-dns-namespace
         additionalEgressConfig:
-        - to:
-          - ipBlock:
-              cidr: 1.2.3.4/32
         - to:
           - podSelector:
               matchLabels:
-                component: ozg-testservice
-          ports:
-            - port: 12345
-              protocol: TCP
-
-        dnsServerNamespace: test-dns-namespace
+                additionalEgressConfig: yes
     asserts:
     - contains:
         path: spec.egress
         content:
           to:
-          - ipBlock:
-              cidr: 1.2.3.4/32
+          - podSelector:
+              matchLabels:
+                additionalEgressConfig: yes
+
+
+  - it: should add additionalIngressConfigNamespace
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-namespace
+        additionalIngressConfigNamespace:
+        - from:
+          - podSelector: 
+              matchLabels:
+                additionalIngressConfigNamespace: yes
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+            - podSelector: 
+                matchLabels:
+                  additionalIngressConfigNamespace: yes
+
+  - it: should add additionalEgressConfigNamespace
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-namespace
+        additionalEgressConfigNamespace:
+        - to:
+          - podSelector:
+              matchLabels:
+                additionalEgressConfigNamespace: yes
+    asserts:
     - contains:
         path: spec.egress
         content:
           to:
           - podSelector:
               matchLabels:
-                component: ozg-testservice
-          ports:
-            - port: 12345
-              protocol: TCP
+                additionalEgressConfigNamespace: yes
 
   - it: test network policy disabled
     set:
diff --git a/nachrichten-manager/pom.xml b/nachrichten-manager/pom.xml
index e569a9df0fd76405975f30c968f21291846c2da2..aeaac44692b56cda777a843885651a86dddccb0c 100644
--- a/nachrichten-manager/pom.xml
+++ b/nachrichten-manager/pom.xml
@@ -24,8 +24,9 @@
     unter der Lizenz sind dem Lizenztext zu entnehmen.
 
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
 
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
@@ -39,278 +40,310 @@
 	<version>2.6.0-SNAPSHOT</version>
 	<name>OZG-Cloud Nachrichten Manager</name>
 
-	<properties>
-		<java.version>21</java.version>
-		<!-- TODO version management -->
-		<shedlock.version>4.25.0</shedlock.version>
-		<logcaptor.version>2.7.10</logcaptor.version>
-		<ozgcloud.license.version>1.3.0</ozgcloud.license.version>
-		<jaxb-maven-plugin.version>3.0.1</jaxb-maven-plugin.version>
-		<ozg-info-manager-interface.version>0.10.0-22.8503e49-20240124.071709-1</ozg-info-manager-interface.version>
-		<bayernid-proxy-interface.version>0.1.0</bayernid-proxy-interface.version>
-	</properties>
+    <properties>
+        <java.version>17</java.version>
+        <!-- TODO version management -->
+        <shedlock.version>4.25.0</shedlock.version>
+        <logcaptor.version>2.7.10</logcaptor.version>
+        <ozgcloud.license.version>1.3.0</ozgcloud.license.version>
+        <jaxb-maven-plugin.version>3.0.1</jaxb-maven-plugin.version>
+        <ozg-info-manager-interface.version>0.10.0-22.8503e49-20240124.071709-1</ozg-info-manager-interface.version>
+        <bayernid-proxy-interface.version>0.1.0</bayernid-proxy-interface.version>
+    </properties>
 
-	<dependencies>
-		<dependency>
-			<groupId>de.ozgcloud.vorgang</groupId>
-			<artifactId>vorgang-manager-base</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>de.ozgcloud.vorgang</groupId>
-			<artifactId>vorgang-manager-interface</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>de.ozgcloud.command</groupId>
-			<artifactId>command-manager</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>de.ozgcloud.vorgang</groupId>
-			<artifactId>vorgang-manager-utils</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>de.ozgcloud.nachrichten</groupId>
-			<artifactId>bayernid-proxy-interface</artifactId>
-			<version>${bayernid-proxy-interface.version}</version>
-		</dependency>
+    <repositories>
+        <repository>
+            <id>shibboleth-releases</id>
+            <name>Shibboleth Releases Repository</name>
+            <url>https://build.shibboleth.net/maven/releases/</url>
+            <releases>
+                <enabled>true</enabled>
+                <checksumPolicy>warn</checksumPolicy>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>shibboleth-thirdparty</id>
+            <name>Shibboleth Thirdparty Repository</name>
+            <url>https://build.shibboleth.net/maven/thirdparty/</url>
+            <releases>
+                <enabled>true</enabled>
+                <checksumPolicy>fail</checksumPolicy>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
 
-		<dependency>
-			<groupId>com.mgmtp.bup.ozg</groupId>
-			<artifactId>ozg-info-manager-interface</artifactId>
-			<version>${ozg-info-manager-interface.version}</version>
-		</dependency>
+    <dependencies>
+        <dependency>
+            <groupId>de.ozgcloud.vorgang</groupId>
+            <artifactId>vorgang-manager-base</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>de.ozgcloud.vorgang</groupId>
+            <artifactId>vorgang-manager-interface</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>de.ozgcloud.command</groupId>
+            <artifactId>command-manager</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>de.ozgcloud.vorgang</groupId>
+            <artifactId>vorgang-manager-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>de.ozgcloud.nachrichten</groupId>
+            <artifactId>bayernid-proxy-interface</artifactId>
+            <version>${bayernid-proxy-interface.version}</version>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-mail</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>com.mgmtp.bup.ozg</groupId>
+            <artifactId>ozg-info-manager-interface</artifactId>
+            <version>${ozg-info-manager-interface.version}</version>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-json</artifactId>
-			<scope>compile</scope>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-web</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-json</artifactId>
+            <scope>compile</scope>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-validation</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web-services</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>org.glassfish.jaxb</groupId>
-			<artifactId>jaxb-runtime</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web-services</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>net.javacrumbs.shedlock</groupId>
-			<artifactId>shedlock-spring</artifactId>
-			<version>${shedlock.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>net.javacrumbs.shedlock</groupId>
-			<artifactId>shedlock-provider-mongo</artifactId>
-			<version>${shedlock.version}</version>
-		</dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>org.mapstruct</groupId>
-			<artifactId>mapstruct</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>net.javacrumbs.shedlock</groupId>
+            <artifactId>shedlock-spring</artifactId>
+            <version>${shedlock.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.shedlock</groupId>
+            <artifactId>shedlock-provider-mongo</artifactId>
+            <version>${shedlock.version}</version>
+        </dependency>
 
-		<!-- grpc -->
-		<dependency>
-			<groupId>net.devh</groupId>
-			<artifactId>grpc-server-spring-boot-starter</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>net.devh</groupId>
-			<artifactId>grpc-client-spring-boot-starter</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
 
-		<!-- commons -->
-		<dependency>
-			<groupId>org.apache.commons</groupId>
-			<artifactId>commons-lang3</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.httpcomponents.client5</groupId>
-			<artifactId>httpclient5</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>commons-beanutils</groupId>
-			<artifactId>commons-beanutils</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-saml2-service-provider</artifactId>
+        </dependency>
 
-		<!-- DEV -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-devtools</artifactId>
-			<scope>runtime</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>org.projectlombok</groupId>
-			<artifactId>lombok</artifactId>
-			<optional>true</optional>
-			<scope>provided</scope>
-		</dependency>
+ 
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-server-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-client-spring-boot-starter</artifactId>
+        </dependency>
 
-		<!-- TEST -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-test</artifactId>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-configuration-processor</artifactId>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>io.github.hakky54</groupId>
-			<artifactId>logcaptor</artifactId>
-			<version>${logcaptor.version}</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>de.ozgcloud.vorgang</groupId>
-			<artifactId>vorgang-manager-base</artifactId>
-			<version>${project.version}</version>
-			<type>test-jar</type>
-			<scope>test</scope>
-		</dependency>
-	</dependencies>
+        <!-- commons -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
 
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-maven-plugin</artifactId>
-				<configuration>
-					<skip>true</skip>
-				</configuration>
-			</plugin>
+        <!-- DEV -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
 
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-jar-plugin</artifactId>
-				<executions>
-					<execution>
-						<goals>
-							<goal>test-jar</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
+        <!-- TEST -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>io.github.hakky54</groupId>
+            <artifactId>logcaptor</artifactId>
+            <version>${logcaptor.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>de.ozgcloud.vorgang</groupId>
+            <artifactId>vorgang-manager-base</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
-			<plugin>
-				<groupId>org.jvnet.jaxb</groupId>
-				<artifactId>jaxb-maven-plugin</artifactId>
-				<version>${jaxb-maven-plugin.version}</version>
-				<configuration>
-					<schemas>
-						<schema>
-							<fileset>
-								<directory>${basedir}/src/main/resources/bayernid</directory>
-								<includes>
-									<include>*.wsdl</include>
-								</includes>
-							</fileset>
-						</schema>
-						<schema>
-							<fileset>
-								<directory>${basedir}/src/main/resources/bayernid</directory>
-								<includes>
-									<include>*.xsd</include>
-								</includes>
-							</fileset>
-						</schema>
-					</schemas>
-					<episode>false</episode>
-				</configuration>
-				<executions>
-					<execution>
-						<goals>
-							<goal>generate</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-			
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-compiler-plugin</artifactId>
-			</plugin>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
 
-			<plugin>
-				<groupId>org.sonarsource.scanner.maven</groupId>
-				<artifactId>sonar-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.jacoco</groupId>
-				<artifactId>jacoco-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-failsafe-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-surefire-plugin</artifactId>
-			</plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
 
-			<plugin>
-				<groupId>com.mycila</groupId>
-				<artifactId>license-maven-plugin</artifactId>
-				<configuration>
-					<mapping>
-						<config>SCRIPT_STYLE</config>
-					</mapping>
-					<licenseSets>
-						<licenseSet>
-							<header>license/eupl_v1_2_de/header.txt</header>
-							<excludes>
-								<exclude>**/README</exclude>
-								<exclude>src/test/resources/**</exclude>
-								<exclude>src/main/resources/**</exclude>
-							</excludes>
-						</licenseSet>
-					</licenseSets>
-				</configuration>
-				<dependencies>
-					<dependency>
-						<groupId>de.ozgcloud.common</groupId>
-						<artifactId>ozgcloud-common-license</artifactId>
-						<version>${ozgcloud.license.version}</version>
-					</dependency>
-				</dependencies>
-			</plugin>
-		</plugins>
-	</build>
+            <plugin>
+                <groupId>org.jvnet.jaxb</groupId>
+                <artifactId>jaxb-maven-plugin</artifactId>
+                <version>${jaxb-maven-plugin.version}</version>
+                <configuration>
+                    <schemas>
+                        <schema>
+                            <fileset>
+                                <directory>${basedir}/src/main/resources/bayernid</directory>
+                                <includes>
+                                    <include>*.wsdl</include>
+                                </includes>
+                            </fileset>
+                        </schema>
+                        <schema>
+                            <fileset>
+                                <directory>${basedir}/src/main/resources/bayernid</directory>
+                                <includes>
+                                    <include>*.xsd</include>
+                                </includes>
+                            </fileset>
+                        </schema>
+                    </schemas>
+                    <episode>false</episode>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
 
-	<distributionManagement>
-		<repository>
-			<id>ozg-nexus</id>
-			<name>ozg-releases</name>
-			<url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
-		</repository>
-		<snapshotRepository>
-			<id>ozg-snapshots-nexus</id>
-			<name>ozg-snapshots</name>
-			<url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
-		</snapshotRepository>
-	</distributionManagement>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.sonarsource.scanner.maven</groupId>
+                <artifactId>sonar-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <configuration>
+                    <mapping>
+                        <config>SCRIPT_STYLE</config>
+                    </mapping>
+                    <licenseSets>
+                        <licenseSet>
+                            <header>license/eupl_v1_2_de/header.txt</header>
+                            <excludes>
+                                <exclude>**/README</exclude>
+                                <exclude>src/test/resources/**</exclude>
+                                <exclude>src/main/resources/**</exclude>
+                            </excludes>
+                        </licenseSet>
+                    </licenseSets>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>de.ozgcloud.common</groupId>
+                        <artifactId>ozgcloud-common-license</artifactId>
+                        <version>${ozgcloud.license.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+    <distributionManagement>
+        <repository>
+            <id>ozg-nexus</id>
+            <name>ozg-releases</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
+        </repository>
+        <snapshotRepository>
+            <id>ozg-snapshots-nexus</id>
+            <name>ozg-snapshots</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
+        </snapshotRepository>
+    </distributionManagement>
 </project>
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4921595ed99fe131abde54ebfccb4f42ad2a8d3e
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import io.grpc.stub.StreamObserver;
+import lombok.RequiredArgsConstructor;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@GrpcService
+@RequiredArgsConstructor
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+class AntragraumGrpcService extends AntragraumServiceGrpc.AntragraumServiceImplBase {
+	private final AntragraumService antragraumService;
+	private final AntragraumNachrichtMapper mapper;
+
+	@Override
+	public void findRueckfragen(GrpcFindRueckfragenRequest request, StreamObserver<GrpcFindRueckfragenResponse> streamObserver) {
+		var rueckfragen = antragraumService.findRueckfragen(request.getSamlToken()).map(mapper::toGrpc).toList();
+		var response = GrpcFindRueckfragenResponse.newBuilder().addAllRueckfrage(rueckfragen).build();
+
+		streamObserver.onNext(response);
+		streamObserver.onCompleted();
+	}
+
+	@Override
+	public void sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest request, StreamObserver<GrpcCommand> streamObserver) {
+		var answer = request.getAnswer();
+
+		var commandId = antragraumService.sendRueckfrageAnswer(request.getSamlToken(), answer.getRueckfrageId(), mapper.toPostfachNachricht(answer));
+
+		streamObserver.onNext(GrpcCommand.newBuilder().setId(commandId).build());
+		streamObserver.onCompleted();
+	}
+}
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapper.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb54984efbb573241ed2aeca1fc2f08fc56d2e8e
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+
+@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
+interface AntragraumNachrichtMapper {
+	String DEFAULT_STATUS = "NEU";
+
+	@Mapping(source = "sentAt", target = "sentAt", dateFormat = "yyyy-MM-dd'T'HH:mm:ss")
+	@Mapping(source = "mailBody", target = "text")
+	@Mapping(source = "subject", target = "vorgangName")
+	@Mapping(source = "attachments", target = "attachmentFileIdList")
+	@Mapping(target = "status", constant = DEFAULT_STATUS)
+	GrpcRueckfrage toGrpc(PostfachNachricht postfachNachricht);
+
+	@Mapping(target = "mailBody", source = "answerText")
+	@Mapping(target = "attachments", source = "attachmentFileIdList")
+	@Mapping(target = "createdAt", ignore = true)
+	@Mapping(target = "createdBy", ignore = true)
+	@Mapping(target = "direction", ignore = true)
+	@Mapping(target = "id", ignore = true)
+	@Mapping(target = "messageCode", ignore = true)
+	@Mapping(target = "messageId", source = "rueckfrageId")
+	@Mapping(target = "postfachAddress", ignore = true)
+	@Mapping(target = "postfachId", ignore = true)
+	@Mapping(target = "replyOption", ignore = true)
+	@Mapping(target = "sentAt", ignore = true)
+	@Mapping(target = "sentSuccessful", ignore = true)
+	@Mapping(target = "subject", ignore = true)
+	@Mapping(target = "vorgangId", ignore = true)
+	PostfachNachricht toPostfachNachricht(GrpcRueckfrageAnswer answer);
+}
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumProperties.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumProperties.java
index 860818fa9c7e82e7331fca1ef06bcb1939d73a8d..9db391f6c208a0aa6c1c1849d9a6c588a031ceb2 100644
--- a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumProperties.java
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumProperties.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
 package de.ozgcloud.nachrichten.antragraum;
 
 import jakarta.validation.constraints.NotEmpty;
@@ -5,6 +28,7 @@ import jakarta.validation.constraints.NotEmpty;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
 import org.springframework.validation.annotation.Validated;
 
 import lombok.Getter;
@@ -23,4 +47,22 @@ public class AntragraumProperties {
 	@NotEmpty
 	private String url;
 
+	/**
+	 * The entityId as defined the BayernId SAML Metadata
+	 */
+	@NotEmpty
+	private String entityId;
+	/**
+	 * The uri where to load the idp Metadata from
+	 */
+	private Resource metadataUri;
+	/**
+	 * The location of the private key for decrypting the saml token data
+	 */
+	private Resource decryptionPrivateKey;
+	/**
+	 * The location of the certificate for decrypting the saml token data
+	 */
+	private Resource decryptionCertificate;
+
 }
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
index 0bbdf0ab26d16ad9f9c14f164f63f241995963f1..3d9ea7abd01eaa507573d2614f599654e0e2bfbf 100644
--- a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/AntragraumService.java
@@ -1,19 +1,50 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
 package de.ozgcloud.nachrichten.antragraum;
 
 import static java.util.Objects.*;
 
-import jakarta.annotation.PostConstruct;
+import java.util.stream.Stream;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.stereotype.Service;
 
 import de.ozgcloud.nachrichten.NachrichtenManagerProperties;
+import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import jakarta.annotation.PostConstruct;
 import lombok.RequiredArgsConstructor;
 
 @Service
 @RequiredArgsConstructor
 @ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
 public class AntragraumService {
+	private final PersistPostfachNachrichtService postfachNachrichtService;
+	private final Saml2Verifier verifier;
+	private final Saml2Parser parser;
+	private final Saml2Decrypter decrypter;
 
 	static final String USER_NOTIFICATION_TEMPLATE = """
 			Guten Tag,
@@ -44,4 +75,28 @@ public class AntragraumService {
 		return USER_NOTIFICATION_TEMPLATE.formatted(getAntragsraumUrl());
 	}
 
+	public Stream<PostfachNachricht> findRueckfragen(String samlToken) {
+		verifyToken(samlToken);
+		var postfachId = decrypter.decryptPostfachId(parser.parse(samlToken));
+
+		return postfachNachrichtService.findRueckfragen(postfachId);
+	}
+
+	public Stream<PostfachNachricht> findRueckfrageAnswers(String rueckfrageId) {
+		return postfachNachrichtService.findAnswers(rueckfrageId);
+	}
+
+	public String sendRueckfrageAnswer(String samlToken, String rueckfrageId, PostfachNachricht nachricht) {
+		verifyToken(samlToken);
+
+		return postfachNachrichtService.persistAnswer(rueckfrageId, nachricht);
+	}
+
+	void verifyToken(String token) {
+		var errors = verifier.verify(token);
+		if (CollectionUtils.isNotEmpty(errors)) {
+			throw new SecurityException("SAML Token verification failed. Errors: %s".formatted(errors));
+		}
+	}
+
 }
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..0961155476fed54722e9572435b34c5225e67e80
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.annotation.PostConstruct;
+
+import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.core.config.InitializationService;
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;
+import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.KeyDescriptor;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;
+import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;
+import org.opensaml.security.credential.impl.CollectionCredentialResolver;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
+import org.opensaml.xmlsec.keyinfo.KeyInfoSupport;
+import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
+import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.security.converter.RsaKeyConverters;
+import org.springframework.security.saml2.Saml2Exception;
+import org.springframework.security.saml2.core.Saml2X509Credential;
+import org.springframework.util.Assert;
+
+import lombok.Getter;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.xml.BasicParserPool;
+import net.shibboleth.utilities.java.support.xml.ParserPool;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
+
+@Configuration
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+class BayernIdSamlConfiguration {
+	private XMLObjectProviderRegistry registry;
+	@Getter
+	private ParserPool parserPool;
+	@Autowired
+	private AntragraumProperties antragraumProperties;
+
+	@PostConstruct
+	void initOpenSAML() {
+		try {
+			registry = new XMLObjectProviderRegistry();
+			ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
+			parserPool = initParserPool();
+
+			registry.setParserPool(parserPool);
+			InitializationService.initialize();
+		} catch (Exception e) {
+			throw new RuntimeException("Initialization failed");
+		}
+	}
+
+	private ParserPool initParserPool() throws ComponentInitializationException {
+		var localParserPool = new BasicParserPool();
+
+		final var features = createFeatureMap();
+
+		localParserPool.setBuilderFeatures(features);
+
+		localParserPool.setBuilderAttributes(new HashMap<>());
+
+		localParserPool.initialize();
+
+		return localParserPool;
+	}
+
+	private Map<String, Boolean> createFeatureMap() {
+		final Map<String, Boolean> features = new HashMap<>();
+		features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
+		features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
+		features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
+		features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
+		features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE);
+		return features;
+	}
+
+	SignatureTrustEngine getTrustEngine() {
+		Set<Credential> credentials = new HashSet<>();
+		Collection<Saml2X509Credential> keys = getCertificatesFromMetadata();
+
+		for (Saml2X509Credential key : keys) {
+			var cred = new BasicX509Credential(key.getCertificate());
+			cred.setUsageType(UsageType.SIGNING);
+			cred.setEntityId(antragraumProperties.getEntityId());
+			credentials.add(cred);
+		}
+
+		CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);
+		return new ExplicitKeySignatureTrustEngine(credentialsResolver,
+				DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
+	}
+
+	CriteriaSet getVerificationCriteria() {
+		var criteria = new CriteriaSet();
+		criteria.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(antragraumProperties.getEntityId())));
+		criteria.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion("urn:oasis:names:tc:SAML:2.0:protocol")));
+		criteria.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
+		return criteria;
+	}
+
+	Saml2X509Credential getDecryptionCredential() {
+		var privateKey = readPrivateKey(antragraumProperties.getDecryptionPrivateKey());
+		var certificate = readCertificateFromResource(antragraumProperties.getDecryptionCertificate());
+		return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
+	}
+
+	private RSAPrivateKey readPrivateKey(Resource location) {
+		Assert.state(location != null, "No private key location specified");
+		Assert.state(location.exists(), () -> "Private key location '" + location + "' does not exist");
+		try (var inputStream = location.getInputStream()) {
+			return RsaKeyConverters.pkcs8().convert(inputStream);
+		} catch (IOException e) {
+			throw new IllegalArgumentException(e);
+		}
+	}
+
+	private X509Certificate readCertificateFromResource(Resource location) {
+		Assert.state(location != null, "No certificate location specified");
+		Assert.state(location.exists(), () -> "Certificate  location '" + location + "' does not exist");
+		try (var inputStream = location.getInputStream()) {
+
+			return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(inputStream);
+		} catch (IOException | CertificateException e) {
+			throw new IllegalArgumentException(e);
+		}
+	}
+
+	List<Saml2X509Credential> getCertificatesFromMetadata() {
+		try (var metadata = antragraumProperties.getMetadataUri().getInputStream()) {
+			var xmlObject = xmlObject(metadata);
+			if (xmlObject instanceof EntitiesDescriptor descriptors) {
+				for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) {
+					if (descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null) {
+						return getVerificationCertificates(descriptor);
+					}
+				}
+			}
+		} catch (IOException e) {
+			throw new Saml2Exception("Error reading idp metadata.", e);
+		} catch (ComponentInitializationException | XMLParserException e) {
+			throw new Saml2Exception("Error initializing parser pool.", e);
+		}
+
+		throw new Saml2Exception("No IDPSSO Descriptors found");
+	}
+
+	private XMLObject xmlObject(InputStream inputStream) throws ComponentInitializationException, XMLParserException {
+		var document = getParserPool().parse(inputStream);
+		var element = document.getDocumentElement();
+		var unmarshaller = this.registry.getUnmarshallerFactory().getUnmarshaller(element);
+		if (unmarshaller == null) {
+			throw new Saml2Exception("Unsupported element of type " + element.getTagName());
+		}
+		try {
+			return unmarshaller.unmarshall(element);
+		} catch (Exception ex) {
+			throw new Saml2Exception(ex);
+		}
+	}
+
+	List<Saml2X509Credential> getVerificationCertificates(EntityDescriptor descriptor) {
+		var idpssoDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
+		if (idpssoDescriptor == null) {
+			throw new Saml2Exception("Metadata response is missing the necessary IDPSSODescriptor element");
+		}
+		List<Saml2X509Credential> verification = new ArrayList<>();
+		for (KeyDescriptor keyDescriptor : idpssoDescriptor.getKeyDescriptors()) {
+			if (keyDescriptor.getUse().equals(UsageType.SIGNING)) {
+				var certificates = certificates(keyDescriptor);
+				for (X509Certificate certificate : certificates) {
+					verification.add(Saml2X509Credential.verification(certificate));
+				}
+			}
+		}
+		if (verification.isEmpty()) {
+			throw new Saml2Exception(
+					"Metadata response is missing verification certificates, necessary for verifying SAML assertions");
+		}
+
+		return verification;
+	}
+
+	private List<X509Certificate> certificates(KeyDescriptor keyDescriptor) {
+		try {
+			return KeyInfoSupport.getCertificates(keyDescriptor.getKeyInfo());
+		} catch (CertificateException ex) {
+			throw new Saml2Exception(ex);
+		}
+	}
+}
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9728ddba81bb0105dde77407ea24b44214e0b29d
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Decrypter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import jakarta.annotation.PostConstruct;
+
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.EncryptedAssertion;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.encryption.Decrypter;
+import org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialSupport;
+import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
+import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
+import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
+import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.security.saml2.Saml2Exception;
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@RequiredArgsConstructor
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+class Saml2Decrypter {
+	private final BayernIdSamlConfiguration configuration;
+
+	public static final String LEGACY_POSTKORB_HANDLE_KEY = "legacyPostkorbHandle";
+	private Decrypter decrypter;
+	private static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(
+			Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),
+					new SimpleRetrievalMethodEncryptedKeyResolver()));
+
+	@PostConstruct
+	void init() {
+		Collection<Credential> credentials = new ArrayList<>();
+		var decryptionX509Credential = configuration.getDecryptionCredential();
+
+		Credential cred = CredentialSupport.getSimpleCredential(decryptionX509Credential.getCertificate(), decryptionX509Credential.getPrivateKey());
+		credentials.add(cred);
+
+		KeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);
+		var setupDecrypter = new Decrypter(null, resolver, encryptedKeyResolver);
+		setupDecrypter.setRootInNewDocument(true);
+
+		decrypter = setupDecrypter;
+	}
+
+	void decryptResponseElements(Response response) {
+		for (EncryptedAssertion encryptedAssertion : response.getEncryptedAssertions()) {
+			try {
+				var assertion = decrypter.decrypt(encryptedAssertion);
+				response.getAssertions().add(assertion);
+			} catch (Exception ex) {
+				throw new Saml2Exception(ex);
+			}
+		}
+	}
+
+	String decryptPostfachId(Response response) {
+		decryptResponseElements(response);
+
+		var samlAssertion = response.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		var postfachIdOptional = attributes.stream().filter(attribute -> LEGACY_POSTKORB_HANDLE_KEY.equals(attribute.getFriendlyName())).findFirst();
+
+		return postfachIdOptional.map(postfachIdAttribute -> {
+			var values = postfachIdAttribute.getAttributeValues();
+			return ((XSString) values.get(0)).getValue();
+		}).orElseThrow();
+
+	}
+}
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa23252ab39606a73825a3981127f0731f0f7e49
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Parser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.security.saml2.Saml2Exception;
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
+
+@Service
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+@RequiredArgsConstructor
+class Saml2Parser {
+	private final BayernIdSamlConfiguration configuration;
+	private ResponseUnmarshaller unmarshaller;
+
+	Response parse(String request) {
+		return (Response) xmlObject(new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8)));
+	}
+
+	XMLObject xmlObject(InputStream inputStream) throws Saml2Exception {
+		try {
+			var document = configuration.getParserPool().parse(inputStream);
+			var element = document.getDocumentElement();
+			return getUnmarshaller().unmarshall(element);
+		} catch (XMLParserException | UnmarshallingException e) {
+			throw new Saml2Exception("Failed to deserialize LogoutRequest", e);
+		}
+	}
+
+	private ResponseUnmarshaller getUnmarshaller() {
+		if (Objects.nonNull(unmarshaller)) {
+			return unmarshaller;
+		}
+
+		unmarshaller = (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory()
+				.getUnmarshaller(Response.DEFAULT_ELEMENT_NAME);
+		return unmarshaller;
+	}
+}
diff --git a/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8cdf4d18e1ce1846294a165a1da80175aef452e
--- /dev/null
+++ b/nachrichten-manager/src/main/java/de/ozgcloud/nachrichten/antragraum/Saml2Verifier.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import jakarta.annotation.PostConstruct;
+
+import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
+import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.security.saml2.core.Saml2Error;
+import org.springframework.security.saml2.core.Saml2ErrorCodes;
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+@Log4j2
+@RequiredArgsConstructor
+@Service
+@ConditionalOnProperty(AntragraumProperties.PROPERTY_ANTRAGSRAUM_URL)
+class Saml2Verifier {
+	public static final String INVALID_SIGNATURE = "Invalid signature for object [%s]: ";
+	public static final String SIGNATURE_MISSING = "Signature missing";
+	private final Saml2Parser parser;
+	private final BayernIdSamlConfiguration configuration;
+	private SignatureTrustEngine trustEngine;
+	private CriteriaSet verificationCriteria;
+
+	@PostConstruct
+	void init() {
+		trustEngine = configuration.getTrustEngine();
+		verificationCriteria = configuration.getVerificationCriteria();
+	}
+
+	List<Saml2Error> verify(String samlToken) {
+		var response = parser.parse(samlToken);
+
+		List<Saml2Error> errors = new ArrayList<>();
+		var signature = response.getSignature();
+		var profileValidator = new SAMLSignatureProfileValidator();
+		if (Objects.nonNull(signature)) {
+			try {
+				profileValidator.validate(signature);
+			} catch (Exception ex) {
+				LOG.error("Error validating SAML Token: ", ex);
+				errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+			}
+
+			try {
+				if (!trustEngine.validate(signature, verificationCriteria)) {
+					errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+				}
+			} catch (Exception ex) {
+				LOG.error("Error validating SAML Token: ", ex);
+				errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, INVALID_SIGNATURE.formatted(response.getID())));
+			}
+		} else {
+			errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, SIGNATURE_MISSING));
+		}
+
+		return errors;
+	}
+}
diff --git a/nachrichten-manager/src/main/resources/application-bayern.yaml b/nachrichten-manager/src/main/resources/application-bayern.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f8fcba293cb9f21fd400b4f86a3c0320c0fa6aa3
--- /dev/null
+++ b/nachrichten-manager/src/main/resources/application-bayern.yaml
@@ -0,0 +1,3 @@
+ozgcloud:
+  antragraum:
+    entityId: https://antragsraum.ozgcloud.de/
\ No newline at end of file
diff --git a/nachrichten-manager/src/main/resources/application-bayernlocal.yaml b/nachrichten-manager/src/main/resources/application-bayernlocal.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..898a6a6d7a85c34656f5ba0a34611a10025f4962
--- /dev/null
+++ b/nachrichten-manager/src/main/resources/application-bayernlocal.yaml
@@ -0,0 +1,7 @@
+ozgcloud:
+  antragraum:
+    url: https://dev.antragsraum.de/
+    entityId: https://antragsraum.ozgcloud.de/
+    metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+    decryptionPrivateKey: "classpath:/bayernid/bayernid-test-enc.key"
+    decryptionCertificate: "classpath:/bayernid/bayernid-test-enc.crt"
\ No newline at end of file
diff --git a/nachrichten-manager/src/main/resources/bayernid/metadata/bayernid-idp-infra.xml b/nachrichten-manager/src/main/resources/bayernid/metadata/bayernid-idp-infra.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ec1ed7ca7099b8be7a8cff7448a740f0b9404c34
--- /dev/null
+++ b/nachrichten-manager/src/main/resources/bayernid/metadata/bayernid-idp-infra.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor entityID="https://infra-pre-id.bayernportal.de/idp">
+        <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+            <md:KeyDescriptor use="signing">
+                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+                            BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD
+                            VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2
+                            MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH
+                            DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq
+                            hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgKWqO2
+                            gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmR
+                            dZjUY2Iui6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+C
+                            Fbo37f9tPwMgf3jgh4gxaujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1
+                            rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFMG7z+Kco2quwOmmZVzWQfeH/3AlN2KbcP
+                            t7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId37MSjoytwBRyd5EACTvhQ
+                            BOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyLdjiy1uCb
+                            jvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGyn
+                            ZmJcZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOE
+                            JFyXIiaqB9J+fcn/biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs
+                            2EG4u1CI2MKDWqK2aEsHrtu8tbS9LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSx
+                            ZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYeltslkaolOmcINXQeSe7nURwp
+                            MB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1UdEwQFMAMBAf8w
+                            DQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM
+                            jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTP
+                            yaHPAZI6e5xHDu8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJ
+                            UyIw5zcKKUQC/JWtMQAQcopbjekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw8
+                            2Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR+Cv3XyzBoGQ/2arijcPnGvth79ff
+                            VFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl92MY+bmugrtTIrazj
+                            tfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq4mJU
+                            Ky/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZh
+                            laZ5ERpWNiv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15
+                            fLXeh2A4YbaBDZdGvDiLOAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpy
+                            LLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ
+                            3emK</ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </md:KeyDescriptor>
+            <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..968c13b46cacfbf6528a6f3ac6d750e0154c08f5
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumGrpcServiceTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import io.grpc.stub.StreamObserver;
+
+class AntragraumGrpcServiceTest {
+	@Spy
+	@InjectMocks
+	private AntragraumGrpcService antragsraumGrpcService;
+	@Mock
+	private AntragraumService antragraumService;
+	@Mock
+	private AntragraumNachrichtMapper mapper;
+
+	@Nested
+	class TestFindRueckfragen {
+
+		@Nested
+		class TestFindRueckfragenGrpc {
+			@Mock
+			private StreamObserver<GrpcFindRueckfragenResponse> streamObserver;
+
+			@BeforeEach
+			void setup() {
+				when(antragraumService.findRueckfragen(any())).thenReturn(Stream.of(PostfachNachrichtTestFactory.create()));
+				when(mapper.toGrpc(any(PostfachNachricht.class))).thenReturn(GrpcRueckfrage.getDefaultInstance());
+			}
+
+			@Test
+			void shouldCallMapper() {
+				antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
+
+				verify(mapper).toGrpc(any(PostfachNachricht.class));
+			}
+
+			@Test
+			void shouldCallOnNext() {
+				antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
+
+				verify(streamObserver).onNext(any(GrpcFindRueckfragenResponse.class));
+			}
+
+			@Test
+			void shouldCallOnCompleted() {
+				antragsraumGrpcService.findRueckfragen(GrpcFindRueckfrageRequestTestFactory.create(), streamObserver);
+
+				verify(streamObserver).onCompleted();
+			}
+		}
+	}
+
+	@Nested
+	class TestSendAnswer {
+		@Mock
+		private StreamObserver<GrpcCommand> streamObserver;
+
+		@BeforeEach
+		void setup() {
+			when(antragraumService.sendRueckfrageAnswer(anyString(), anyString(), any(PostfachNachricht.class)))
+					.thenReturn(UUID.randomUUID().toString());
+			when(mapper.toPostfachNachricht(any(GrpcRueckfrageAnswer.class))).thenReturn(PostfachNachrichtTestFactory.create());
+		}
+
+		@Test
+		void shouldCallMapper() {
+			antragsraumGrpcService.sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequestTestFactory.create(), streamObserver);
+
+			verify(mapper).toPostfachNachricht(any(GrpcRueckfrageAnswer.class));
+		}
+
+		@Test
+		void shouldCallAntragraumService() {
+			antragsraumGrpcService.sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequestTestFactory.create(), streamObserver);
+
+			verify(antragraumService).sendRueckfrageAnswer(anyString(), anyString(), any(PostfachNachricht.class));
+		}
+
+		@Test
+		void shouldCallOnNext() {
+			antragsraumGrpcService.sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequestTestFactory.create(), streamObserver);
+
+			verify(streamObserver).onNext(any(GrpcCommand.class));
+		}
+
+		@Test
+		void shouldCallOnCompleted() {
+			antragsraumGrpcService.sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequestTestFactory.create(), streamObserver);
+
+			verify(streamObserver).onCompleted();
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ff78a92ed2d37b75e8c380ea47c87d41a50790c
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumNachrichtMapperTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.format.DateTimeFormatter;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
+import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory;
+
+class AntragraumNachrichtMapperTest {
+
+	AntragraumNachrichtMapper mapper = Mappers.getMapper(AntragraumNachrichtMapper.class);
+
+	@Nested
+	class TestMapRueckfrage {
+
+		@Test
+		void shouldMapVorgangId() {
+			var result = map();
+
+			assertThat(result.getVorgangId()).isEqualTo(MessageTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldMapId() {
+			var result = map();
+
+			assertThat(result.getId()).isEqualTo(PostfachNachrichtTestFactory.ID);
+		}
+
+		@Test
+		void shouldMapVorgangName() {
+			var result = map();
+
+			assertThat(result.getVorgangName()).isEqualTo(MessageTestFactory.SUBJECT);
+		}
+
+		@Test
+		void shouldMapStatus() {
+			var result = map();
+
+			assertThat(result.getStatus()).isEqualTo(AntragraumNachrichtMapper.DEFAULT_STATUS);
+		}
+
+		@Test
+		void shouldMapSentAt() {
+			var result = map();
+
+			assertThat(result.getSentAt()).isEqualTo(PostfachNachrichtTestFactory.SENT_AT.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
+		}
+
+		@Test
+		void shouldMapText() {
+			var result = map();
+
+			assertThat(result.getText()).isEqualTo(PostfachNachrichtTestFactory.MAIL_BODY);
+		}
+
+		@Test
+		void shouldMapAttachments() {
+			var result = map();
+
+			assertThat(result.getAttachmentFileIdCount()).isEqualTo(1);
+			assertThat(result.getAttachmentFileId(0)).isEqualTo(PostfachNachrichtTestFactory.ATTACHMENTS.get(0));
+		}
+
+		private GrpcRueckfrage map() {
+			return mapper.toGrpc(PostfachNachrichtTestFactory.create());
+		}
+
+	}
+
+	@Nested
+	class TestMapAnswerToPostfachNachricht {
+		@Test
+		void shouldMapText() {
+			var result = map();
+
+			assertThat(result.getMailBody()).isEqualTo(GrpcRueckfrageAnswerTestFactory.TEXT);
+		}
+
+		@Test
+		void shouldMapMessageId() {
+			var result = map();
+
+			assertThat(result.getMessageId()).isEqualTo(GrpcRueckfrageAnswerTestFactory.RUECKFRAGE_ID);
+		}
+
+		@Test
+		void shouldMapAttachmentIds() {
+			var result = map();
+
+			assertThat(result.getAttachments()).isEqualTo(GrpcRueckfrageAnswerTestFactory.ATTACHMENT_ID_LIST);
+		}
+
+		private PostfachNachricht map() {
+			return mapper.toPostfachNachricht(GrpcRueckfrageAnswerTestFactory.create());
+		}
+	}
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
index fb81269bda54f4e645399c75c7cf666bfaeaa11c..1b8cfe1ed00be12d1d23043ef422878b7283f254 100644
--- a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumServiceTest.java
@@ -1,19 +1,25 @@
 package de.ozgcloud.nachrichten.antragraum;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+import java.util.UUID;
+
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Spy;
+import org.opensaml.saml.saml2.core.Response;
 
 import de.ozgcloud.nachrichten.NachrichtenManagerProperties;
-import de.ozgcloud.nachrichten.antragraum.AntragraumProperties;
-import de.ozgcloud.nachrichten.antragraum.AntragraumService;
+import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory;
 
 class AntragraumServiceTest {
-
+	@Spy
 	@InjectMocks
 	private AntragraumService service;
 
@@ -21,6 +27,14 @@ class AntragraumServiceTest {
 	private AntragraumProperties properties;
 	@Mock
 	private NachrichtenManagerProperties nachrichtenManagerProperties;
+	@Mock
+	private PersistPostfachNachrichtService postfachNachrichtService;
+	@Mock
+	private Saml2Verifier verifier;
+	@Mock
+	private Saml2Parser parser;
+	@Mock
+	private Saml2Decrypter decrypter;
 
 	@Nested
 	class TestGetAntragsraumUrl {
@@ -38,4 +52,68 @@ class AntragraumServiceTest {
 
 	}
 
+	@Nested
+	class TestFindRueckfragen {
+		static final String SAML_TOKEN = "TOKEN";
+
+		@Test
+		void shouldCallVerify() {
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(service).verifyToken(anyString());
+		}
+
+		@Test
+		void shouldCallVerifier() {
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(verifier).verify(anyString());
+		}
+
+		@Test
+		void shouldCallDecrypt() {
+			when(parser.parse(anyString())).thenReturn(mock(Response.class));
+
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(decrypter).decryptPostfachId(any(Response.class));
+		}
+
+		@Test
+		void shouldCallPostfachService() {
+			when(decrypter.decryptPostfachId(any())).thenReturn(GrpcFindRueckfrageRequestTestFactory.POSTFACH_ID);
+
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(postfachNachrichtService).findRueckfragen(anyString());
+		}
+	}
+
+	@Nested
+	class TestSendRueckfragenAnswers {
+		static final String SAML_TOKEN = "TOKEN";
+		static final String RUECKFRAGE_ID = UUID.randomUUID().toString();
+		static final PostfachNachricht NACHRICHT = PostfachNachrichtTestFactory.create();
+
+		@Test
+		void shouldCallVerify() {
+			service.sendRueckfrageAnswer(SAML_TOKEN, RUECKFRAGE_ID, NACHRICHT);
+
+			verify(service).verifyToken(anyString());
+		}
+
+		@Test
+		void shouldCallVerifier() {
+			service.findRueckfragen(SAML_TOKEN);
+
+			verify(verifier).verify(anyString());
+		}
+
+		@Test
+		void shouldCallPostfachService() {
+			service.sendRueckfrageAnswer(SAML_TOKEN, RUECKFRAGE_ID, NACHRICHT);
+
+			verify(postfachNachrichtService).persistAnswer(anyString(), any(PostfachNachricht.class));
+		}
+	}
 }
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..045f5caf46429366373499cfd279563895d3d9ad
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.core.io.InputStreamResource;
+
+import de.ozgcloud.common.test.TestUtils;
+
+class BayernIdSamlConfigurationTest {
+	@Mock
+	private AntragraumProperties properties;
+	@InjectMocks
+	private BayernIdSamlConfiguration bayernIdSamlConfiguration;
+
+	@Nested
+	class TestCreationOfParserPool {
+		@BeforeEach
+		void setup() throws Exception {
+			bayernIdSamlConfiguration.initOpenSAML();
+		}
+
+		@Test
+		void shouldCreateParserPool() {
+			assertThat(bayernIdSamlConfiguration.getParserPool()).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestCreatingTrustEngine {
+		@BeforeEach
+		void setup() throws Exception {
+			when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+			when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/");
+			bayernIdSamlConfiguration.initOpenSAML();
+		}
+
+		@Test
+		void shouldCreateTrustEngine() {
+			assertThat(bayernIdSamlConfiguration.getTrustEngine()).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestLoadingVerficationData {
+		@Test
+		void shouldCreateVerificationCriteria() {
+			when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/");
+			bayernIdSamlConfiguration.initOpenSAML();
+
+			assertThat(bayernIdSamlConfiguration.getVerificationCriteria()).isNotNull();
+		}
+
+		@Test
+		void shouldLoadVerificationCert() throws Exception {
+			when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+			bayernIdSamlConfiguration.initOpenSAML();
+
+			assertThat(bayernIdSamlConfiguration.getCertificatesFromMetadata()).isNotNull();
+		}
+	}
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFindRueckfrageRequestTestFactory.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFindRueckfrageRequestTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..9795428ab0d6020b63251d765af3e25e0f89cfa4
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcFindRueckfrageRequestTestFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import de.ozgcloud.common.test.TestUtils;
+
+import java.util.UUID;
+
+public class GrpcFindRueckfrageRequestTestFactory {
+	static final String POSTFACH_ID = UUID.randomUUID().toString();
+	static final String SAML_TOKEN = TestUtils.loadTextFile("SamlResponse.xml");
+
+	static GrpcFindRueckfragenRequest create() {
+		return createBuilder().build();
+	}
+
+	static GrpcFindRueckfragenRequest.Builder createBuilder() {
+		return GrpcFindRueckfragenRequest.newBuilder()
+				.setPostfachId(POSTFACH_ID)
+				.setSamlToken(SAML_TOKEN);
+	}
+}
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcRueckfrageAnswerTestFactory.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcRueckfrageAnswerTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c0fb28f113bc5e179aff00d19a9e5cd5a7a6cc9
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcRueckfrageAnswerTestFactory.java
@@ -0,0 +1,23 @@
+package de.ozgcloud.nachrichten.antragraum;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.thedeanda.lorem.LoremIpsum;
+
+public class GrpcRueckfrageAnswerTestFactory {
+	static final String RUECKFRAGE_ID = UUID.randomUUID().toString();
+	static final String TEXT = LoremIpsum.getInstance().getParagraphs(2, 4);
+	static final List<String> ATTACHMENT_ID_LIST = List.of(UUID.randomUUID().toString());
+
+	static GrpcRueckfrageAnswer create() {
+		return createBuilder().build();
+	}
+
+	static GrpcRueckfrageAnswer.Builder createBuilder() {
+		return GrpcRueckfrageAnswer.newBuilder()
+				.setRueckfrageId(RUECKFRAGE_ID)
+				.setAnswerText(TEXT)
+				.addAllAttachmentFileId(ATTACHMENT_ID_LIST);
+	}
+}
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcSendRueckfrageAnswerRequestTestFactory.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcSendRueckfrageAnswerRequestTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1fa9bcfd3877d5bdc17b611f06d373feb565205
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/GrpcSendRueckfrageAnswerRequestTestFactory.java
@@ -0,0 +1,19 @@
+package de.ozgcloud.nachrichten.antragraum;
+
+import de.ozgcloud.common.test.TestUtils;
+
+public class GrpcSendRueckfrageAnswerRequestTestFactory {
+	static final String SAML_TOKEN = TestUtils.loadTextFile("SamlResponse.xml");
+	static final GrpcRueckfrageAnswer ANSWER = GrpcRueckfrageAnswerTestFactory.create();
+
+	static GrpcSendRueckfrageAnswerRequest create() {
+		return createBuilder().build();
+	}
+
+	static GrpcSendRueckfrageAnswerRequest.Builder createBuilder() {
+		return GrpcSendRueckfrageAnswerRequest.newBuilder()
+				.setAnswer(ANSWER)
+				.setSamlToken(SAML_TOKEN);
+	}
+
+}
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1dd94cea3e8ef54e5b7d62ac936921ed583d748b
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2DecrypterTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.Response;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import de.ozgcloud.common.test.TestUtils;
+
+@Disabled("Passende certificat und key file muessen local vorhanden sein")
+class Saml2DecrypterTest {
+	private Response samlResponse;
+
+	@Mock
+	private AntragraumProperties properties;
+
+	private BayernIdSamlConfiguration configuration;
+
+	private Saml2Decrypter decrypter;
+
+	@BeforeEach
+	void setup() throws Exception {
+		when(properties.getDecryptionCertificate()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-test-enc.crt")));
+		when(properties.getDecryptionPrivateKey()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-test-enc.key")));
+
+		configuration = new BayernIdSamlConfiguration();
+		ReflectionTestUtils.setField(configuration, "antragraumProperties", properties);
+		configuration.initOpenSAML();
+
+		var samlResponseString = TestUtils.loadTextFile("SamlResponse.xml");
+		var parser = new Saml2Parser(configuration);
+		samlResponse = parser.parse(samlResponseString);
+
+		decrypter = new Saml2Decrypter(configuration);
+		decrypter.init();
+	}
+
+	@Test
+	void shouldDecrypt() {
+		assertThat(samlResponse.getAssertions()).isEmpty();
+
+		decrypter.decryptResponseElements(samlResponse);
+
+		assertThat(samlResponse.getAssertions()).isNotEmpty();
+	}
+
+	@Test
+	void shouldHaveSubject() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+
+		assertThat(samlAssertion.getSubject()).isNotNull();
+	}
+
+	@Test
+	void shouldHaveAuthnStatements() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var authnStatements = samlAssertion.getAuthnStatements();
+
+		assertThat(authnStatements).isNotNull();
+	}
+
+	@Test
+	void shouldHaveStatements() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = samlAssertion.getStatements();
+
+		assertThat(statements).isNotNull();
+	}
+
+	@Test
+	void shouldHaveAttributes() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		assertThat(attributes).hasSize(7);
+	}
+
+	@Test
+	void shouldHavePostfachId() {
+		decrypter.decryptResponseElements(samlResponse);
+		var samlAssertion = samlResponse.getAssertions().get(0);
+		var statements = (AttributeStatement) samlAssertion.getStatements().get(1);
+		var attributes = statements.getAttributes();
+		var postfachIdOptional = attributes.stream().filter(attribute -> "legacyPostkorbHandle".equals(attribute.getFriendlyName())).findFirst();
+		assertThat(postfachIdOptional).map(Attribute::getAttributeValues).isNotNull();
+	}
+
+	@Test
+	void shouldGetPostfachId() {
+		var postfachId = decrypter.decryptPostfachId(samlResponse);
+
+		assertThat(postfachId).isEqualTo("28721c6f-b78f-4d5c-a048-19fd2fc429d2");
+	}
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..afcca2fbe9c930eebf72c015495ac38f3a3ed2bd
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2ParserTest.java
@@ -0,0 +1,72 @@
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opensaml.saml.saml2.core.Response;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import de.ozgcloud.common.test.TestUtils;
+
+class Saml2ParserTest {
+	private BayernIdSamlConfiguration configuration;
+
+	@BeforeEach
+	void setup() {
+		var properties = mock(AntragraumProperties.class);
+		configuration = new BayernIdSamlConfiguration();
+		ReflectionTestUtils.setField(configuration, "antragraumProperties", properties);
+		configuration.initOpenSAML();
+	}
+
+	@Test
+	void shouldInit() {
+		var parser = new Saml2Parser(configuration);
+
+		assertThat(parser).isNotNull();
+	}
+
+	@Test
+	void shouldParseSamlToken() throws Exception {
+		var response = getResponse();
+		assertThat(response).isNotNull();
+	}
+
+	@Test
+	void shouldHaveAssertions() throws Exception {
+		var response = getResponse();
+		assertThat(response.getAssertions()).isNotNull();
+	}
+
+	@Test
+	void shouldHaveEncryptedAssertions() throws Exception {
+		var response = getResponse();
+		assertThat(response.getEncryptedAssertions()).isNotNull();
+	}
+
+	@Test
+	void shouldHaveIssuer() throws Exception {
+		var response = getResponse();
+		assertThat(response.getIssuer().getValue()).isEqualTo("https://infra-pre-id.bayernportal.de/idp");
+	}
+
+	@Test
+	void shouldGetXMLObject() throws Exception {
+		var parser = new Saml2Parser(configuration);
+
+		try (var tokenStream = TestUtils.loadFile("SamlResponse.xml")) {
+			assertThat(parser.xmlObject(tokenStream)).isNotNull();
+		}
+	}
+
+	private Response getResponse() throws Exception {
+		var token = TestUtils.loadTextFile("SamlResponse.xml");
+		var parser = new Saml2Parser(configuration);
+
+		return parser.parse(token);
+	}
+
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..03dbcb5983d097f1516bdea5bc6f284345d43b15
--- /dev/null
+++ b/nachrichten-manager/src/test/java/de/ozgcloud/nachrichten/antragraum/Saml2VerifierTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2024.
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+
+package de.ozgcloud.nachrichten.antragraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import de.ozgcloud.common.test.TestUtils;
+
+class Saml2VerifierTest {
+	private String samlResponse;
+
+	private Saml2Verifier verifier;
+
+	@Mock
+	private AntragraumProperties properties;
+
+	private BayernIdSamlConfiguration configuration;
+
+	@BeforeEach
+	void setup() throws Exception {
+		when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml")));
+		when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de");
+		samlResponse = TestUtils.loadTextFile("SamlResponse.xml");
+
+		configuration = new BayernIdSamlConfiguration();
+		ReflectionTestUtils.setField(configuration, "antragraumProperties", properties);
+		configuration.initOpenSAML();
+
+		var parser = new Saml2Parser(configuration);
+
+		verifier = new Saml2Verifier(parser, configuration);
+		verifier.init();
+	}
+
+	@Test
+	void shouldGetVerificationError() {
+		var res = verifier.verify(samlResponse);
+
+		assertThat(res).isNotEmpty();
+	}
+}
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/resources/SamlResponse.xml b/nachrichten-manager/src/test/resources/SamlResponse.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d28738585aa627bc0bd4e6b7288ff3346ea4029e
--- /dev/null
+++ b/nachrichten-manager/src/test/resources/SamlResponse.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2024.
+  ~ Lizenziert unter der EUPL, Version 1.2 oder - sobald
+  ~ diese von der Europäischen Kommission genehmigt wurden -
+  ~ Folgeversionen der EUPL ("Lizenz");
+  ~ Sie dürfen dieses Werk ausschließlich gemäß
+  ~ dieser Lizenz nutzen.
+  ~ Eine Kopie der Lizenz finden Sie hier:
+  ~
+  ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+  ~
+  ~ Sofern nicht durch anwendbare Rechtsvorschriften
+  ~ gefordert oder in schriftlicher Form vereinbart, wird
+  ~ die unter der Lizenz verbreitete Software "so wie sie
+  ~ ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+  ~ ausdrücklich oder stillschweigend - verbreitet.
+  ~ Die sprachspezifischen Genehmigungen und Beschränkungen
+  ~ unter der Lizenz sind dem Lizenztext zu entnehmen.
+  -->
+
+<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
+                 Destination="https://deep-touching-condor.ngrok-free.app/login/saml2/sso/bayernid"
+                 ID="_d75103771f4e3869ca4bf743efb51320" InResponseTo="ARQf371368-b6eb-4708-b90d-e8a9c5fc0ffd"
+                 IssueInstant="2024-02-07T10:27:18.456Z" Version="2.0">
+    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://infra-pre-id.bayernportal.de/idp
+    </saml2:Issuer>
+    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+        <ds:SignedInfo>
+            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+            <ds:Reference URI="#_d75103771f4e3869ca4bf743efb51320">
+                <ds:Transforms>
+                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                </ds:Transforms>
+                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+                <ds:DigestValue>y8O2/uKwgap3hb7Ym/sn+v0e3l+w0Z+wIFe11xXkSHU=</ds:DigestValue>
+            </ds:Reference>
+        </ds:SignedInfo>
+        <ds:SignatureValue>
+            G4fZCUS7Z+7PwDF7J+ZwXssM+iHBgxt34Uf4U3PWrbaYROrCZFD3hlVwCj35Z7RlQkDBi4q1m9RW6XGGVx2vpUDjT9dTkfbF7tB9PXa6l4mq3RyxuRELMwRpcbnamfe02qwtp0N7n9+gdjTVPb2xTMhp7FVG3OZ46OKwwJIm6jNLE+zVbKkNmxnv8XqGK+FgDS82CCG6Zi8nIZZkR80vHuRnSwrpStiInWSURoIYvG8nQfJ6u6IxbtMkDPtLrQHP6th9NMEyODe4RrjNwH8ERkbBl+rvtz406y3hngOW4uxNSTdQGOWj68t7LSn78S+Zc+5g/8up8gRIY6FWB5QxTl+GINIskcoWEfpyQcY932Jh9jGKFRBj2bcP0xALOeP+LTAz1O3hY0EZD0HpjILNhjp4/4Ki6SSeoVrp4UdEZGPpfFAMXdA9unjQGf5DqT3los5mH+KgkpAQoIU0725tIJuGojigXDIKgbNftB1oXjepcqcWvdnbRZlE9Kk4iU2YcVKGxHtEGi03+Qr2M37SqnooXw94Q0LxOQHU0jaOuw+nA8JbcvbpmHVbh7Qyg6OfrI/g+1pwhaQWrL6zEDDlgF3Fj6QxZGhMviCf43WJd8nPPwLIp0dFxXmbX5yBnpAPC4txJkf4idH8gze054O0Zf9G35vFH8oxELrA+d3qbPY=
+        </ds:SignatureValue>
+        <ds:KeyInfo>
+            <ds:X509Data>
+                <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNVBAYTAkRFMQ8w
+                    DQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYDVQQKDARBS0RCMQwwCgYDVQQL
+                    DANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0G
+                    A1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwD
+                    SURNMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgK
+                    WqO2gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmRdZjUY2Iu
+                    i6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+CFbo37f9tPwMgf3jgh4gx
+                    aujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFM
+                    G7z+Kco2quwOmmZVzWQfeH/3AlN2KbcPt7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId3
+                    7MSjoytwBRyd5EACTvhQBOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyL
+                    djiy1uCbjvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGynZmJc
+                    ZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOEJFyXIiaqB9J+fcn/
+                    biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs2EG4u1CI2MKDWqK2aEsHrtu8tbS9
+                    LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSxZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYe
+                    ltslkaolOmcINXQeSe7nURwpMB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1Ud
+                    EwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM
+                    jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTPyaHPAZI6e5xH
+                    Du8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJUyIw5zcKKUQC/JWtMQAQcopb
+                    jekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw82Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR
+                    +Cv3XyzBoGQ/2arijcPnGvth79ffVFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl9
+                    2MY+bmugrtTIrazjtfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq
+                    4mJUKy/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZhlaZ5ERpW
+                    Niv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15fLXeh2A4YbaBDZdGvDiL
+                    OAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpyLLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE
+                    1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ3emK
+                </ds:X509Certificate>
+            </ds:X509Data>
+        </ds:KeyInfo>
+    </ds:Signature>
+    <saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
+        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+    </saml2p:Status>
+    <saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
+        <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_baed1174200b81b1bff3856cb4e6365c"
+                            Type="http://www.w3.org/2001/04/xmlenc#Element">
+            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
+                                   xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
+            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                <xenc:EncryptedKey Id="_5a164760d15a61d269e1f7fdd9872a10" Recipient="https://antragsraum.ozgcloud.de/"
+                                   xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+                    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
+                                           xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
+                                         xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
+                    </xenc:EncryptionMethod>
+                    <ds:KeyInfo>
+                        <ds:X509Data>
+                            <ds:X509Certificate>
+                                MIIDsTCCApmgAwIBAgIUdw/27be5+2vj+MhGtoJjDsMsdDEwDQYJKoZIhvcNAQELBQAwaDELMAkG
+                                A1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xDzANBgNVBAoMBm1n
+                                bSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVzZUBtZ20tdHAuY29tMB4XDTI0MDExNjEyMjI0
+                                OVoXDTI1MDExNTEyMjI0OVowaDELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UE
+                                BwwITXVlbmNoZW4xDzANBgNVBAoMBm1nbSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVzZUBt
+                                Z20tdHAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/HBBWBDSrEgdwXkSy15V
+                                00EaVTyLgc4vh/JcDiGIYZSqmcMwBd+B1u36xbdBf/duEtCUymMNP48OMjgFZtR6xn0meuR4NR6Y
+                                kn9mYGdU/GhldGuGv9XLAEAkVuTlo0H1QYyBS/6JwKQoSsHDkJ3YwDwKcyOt7QtpSadRZjQEN3gD
+                                vWoRYjgXTxj2I1ovllmi0zOHsFi5PBIuiPWUdJvBrHxpD/XVS9R/qzJpHPu3bjQ6UVRmhiZCUF7H
+                                5F/PQNwk+qXvjV0ooBeSWWO5hywhk4OP4QEgbYMOSo20YukYX8TJEsum1pwIcQrw7kW4GyKaAycy
+                                Rsa1fbM3tEkj+TiBKwIDAQABo1MwUTAdBgNVHQ4EFgQUfDL/6R33SJodsONCvxKy96AtU18wHwYD
+                                VR0jBBgwFoAUfDL/6R33SJodsONCvxKy96AtU18wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+                                AQsFAAOCAQEA+PCnvSwKU+bArTCIg5lfrwONbzKkjvPUymDNYX3oj1wVEN75hNf0RD7Rr0//ZYT3
+                                Rt0G193gjDcH1gbGIYhMLeGGkxEous2l3O+pRIQRR+hprjr6HzF8IphaJy1RbDwyGsXyLcyOylPL
+                                4cX9IjUdhklHiLZusBq95LSyw7hsCOAL2+vn816O7yv+28EWXXbnP2XEUjW36nxcZvR6oTJUplXy
+                                HRuuJJTsOxGRNuXA3UVgNbkdm1HnoSGpnsGdUKsUFoEmEJkcSdQRwxeH21WzYGOZmKMcvx2gObaS
+                                P8tafWh5z4Jx+Z7z5WP72Jt44/lnVjaV8aGo0KHXwgqQOtYftQ==
+                            </ds:X509Certificate>
+                        </ds:X509Data>
+                    </ds:KeyInfo>
+                    <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+                        <xenc:CipherValue>
+                            ffr9pG/yL4QGQ4o1z/t6HH5XRG8pMHHjzlVTq6uC4eRpVvaNMz8XpUXqNAFGiB0Xbpkm++qOhGsOuz5Wffq5Qo78fMBfU95L1Lk9cVH1pUFfYyz5GV1LqlhStAZrCGHUdv5d0O7JLKgbi45JxxTc7ErAwPlOMqKLs95ZJuhl8Fp9XcYrdzW9IjuwmkB/HyPyjBWV066gaCMLImeBdCzBZc0pxuvH9jq8eX7h1B1eCd5F1LIoj35YDeU3PA/P/E6tLBxdGLFws+nYqNU3B5R2FPPoW+LP9zM7Q+SR20ti1Uh6TEMha05sJjWXFJU78PpJAtEl978ifqqO/23lYXYCrA==
+                        </xenc:CipherValue>
+                    </xenc:CipherData>
+                </xenc:EncryptedKey>
+            </ds:KeyInfo>
+            <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
+                <xenc:CipherValue>
+                    YvHcO5br8vWMo/vreVcU8MQylgUmU6vxmvEoUJ07+8kSV0prHD46TtuXHXsLnsYKQMs+SM6bnTWMooNxIy8b0/BvIG4lg/l3m+SwoP56+lOMx26mTEwz9dJT+4BTFH25i3GbWQd86AbD4wT2X9juExE1t6uq1bhACqgeA1erhy/fjTS+1fKKbOTdC0DF35idejruc/YFyV/BNRMHncfj5BnFQFUmXT0b2Fg+WyUAIzflqwXmUhdzmQNl2uqZDL2Vaw4vQp1a9S0ePD3t+QCSGtCYcmqvg+FhooS1KfhqITNinzi07KaQNW6sY0uXpnq3/BGWxXrU58rmU24AQ5ozAgwPfpmOnZhyUURSikvKmvcpD8DPy8jMOxi+rd/tYlxokjPjFSV8ixh0fGZl+7mDAt/YvxBOSSln3nUEMVoKMNgUT3ax6cyUTBXOxt1EMLYuAcHRIBpF2R9MYasmaaR1RntlkKPiomaPdGqIgh1HtkmuwkS6npAH68d0ObD/SpRqu+vPuu++RxHPlbIjShbJWYUJIqPJlhtG7/p9VkC3514t/H1cgxALKstC95OXNH8S6b4m+atl1Wf4PELfHD7lT/QgYHwZk2/xhoWduKc18nauB4kc3v3FZ8nIO3iCgBkXT8qq+/1wJgOnVqAEFWs8w8AC99gHoTgm04q+JUw9XramIXEOdJ10/2Y8HW97XCt53KG+kbaJZe7DISx+2eh79ed3BxEzwgqcXlsrIVcezXWJAl2myFxUggn3c/N1ckgOWFwENOSDFXS6WAtSo/8dlXYvgZTYyKVvVO7l5oq5al2ESrFkHqB3WKu0q9LaWEKw7jMy6FnOawSOjJ2t5o43hfqs9+Wg3J7bgM8YjDuuNZIJ55SO7NIdeeFOkkgGMgcmF0svsxGKbHhdkvqa0t0BLtase7dNIgfWY5laXU7xBn+HEhrz9ZR1xnk8gK28KZwR1wO72rpcV9igsbek5cxEJ/hAk9NSHw7VXAgXZJ6VKZapJfoOqanE50FfK8z0TaoKBUbc1kfOAFnB0n9nUKpUXyyqfiu12w4n3lAs3tBB2S4w3UW5Mef1ZGdF8dqs/T0GUs819vrwYENVUM8u1usP44MnMdTw2i3eo6/VYrCOo1tsa31t46BbLEnercEdGDgpOOBjuda+C3G4NtlDEFzM8rN2FspLS5wphCvKiTB867Q9nrsXOQdDTiwXs2wOpVq1utASdV82Yam1ucVVfriDWlLlhFsFpZnUmwG4zwCPqah2t8inSwx9jramTbJnXpihRw75mFGFC5QAalnV/EAtZ58ecIz4PVTRq4zP7OKtwnN+qIOMm//8AOl6e5H/At7qWB5/dVjw8BUdIxaUF+e33NdQG6x8IEbMhlGFTGCjv5E0h27D/AbqCBU6PKrkGZowZHtKNtz7nITysa0hpyefM0rQkFHwf4NmR8uSHZ9YoT8VLStTtrF6daHSYlUT45EDXkiSA7NCAX8BIDbZNGxPGNCWR3reZxtQoJ89YPPdQngyqMn7sB1T4sP+euWpnAOOF5VDA2xyM2WtPzuKDZFD0XH5r3AqKf2jl+E1/jSBlhW1Y677WVLTLJm7+lMYYLK6YVaSsKQp10lOdhcNSoNF2iWUwmdCTfkrYIqM6Dz6FiYSrg1TTlOJb7Mg/z54eO6utHzIRFepBTL+pu9qP38Y21NtpUWu//jh0vpniXu1rzGQvuiuoNGIiqYA/dL39cpLB5JIwEVWNsnlHvzRcYQnqg2mhdkFMNZPhZwb3f+BV3GNA/dTziazHEgUN5G0esGl85PlZM7oLrPkXe1mYKh3pVRVHx9bScTQ/KR/Dll0NI1d5ul2SZa80Pmj29voelfMUa5Ml5q4zHzXK6N1E70ujMzWWJB025gLIb3e7aE9eN/5SJegL2ctob8uNQCTSouel6ko+Ykw/pstPL8IYWgsAwrckmzNJHasZi9MB9re639Zf+bDvflpigNboH6Jgzs7us5C2dozH1s5IzGP4XzgeALu8XP/T2coQxhj9xfaAJFrtqWRse9MM9gKIWCBEIzzIPeMa0+S7QAKAd7XRS/oZSDeqUFK2jRkrpbEnUxRErfX6lmHWlaY4CU8XKbSYU13LkuJcyzCm9EN/BU7f5cOwaVwSLMfOQeOx0GwJ5KRF4F30pVQIKfQmXt627TEmrutHr2mIgBLcte+S052JdumcRFMnlNLlmpl/rnOrU0bBeNDtXHQuYCWwfgiyZ47J2OZ+R68v0lA8mSdHFtJ81mbGnYQZn6PnZ/gbKz8uTu5WZE6CQ+n8IY6uvd8B0EYOjaoJK/FTQRjnYe6cBWTQFfmFzH441Ao3vcfKh6co4d5Ec1L9PXQWXidhCv4aJw1G06zgOTIcLCqvE3rbBpYOFUOmKlQbBmaIiKWqG+Xqk34jbQgzfHmcFO96sqh6B64/OzjPdjQupzCTM9hBNwSUABWoOAb9Yn0wtCUU5tRMTXf2DessFKbm5Jygvbh7KrBIQX3SGLQ1sHY+MYVbDpRTyi6/cATCCSWcekyT08mEh52KDKbrTR4CFdu2B7wsIqKxoiW4XiHOOc7DGtgVaCXgoce0nd9YXrtXbIjriMddx7wo/A5xvQbTOyaBuL/j3893Ny6eU10c18p8tZCzdBO1VOWH+R+8TkdvhkA995xzrdvjw7RoSdO7eqdyiOqkHBJGAp+OVSMWf0+/kHKRQf3LTT+CcwYN+7mx+Qz6Uw6Squ6/9JsxsEfIQ3VRsa84hUjcmmKf4QTpzy54RIteqPZc0kNQPobpKmvyWUUjldV0jgYi98XfErrdLQF3gyU/TG0kOsEPjuskIHoLEEka+nGXOjbtxj7HZe7onJ54kIluMkHeKfLWOMoZ2Qx9tqYMoGtbvLf6RrKh7METcaenirAS0waZfYWbIKS/XQQnlG0kWmK2VbUtKpKwAkjlEFEkYo8NtYRTqB1gYp7iyOhOcYT8Hdd6y+276P+sF+FuuXBAd1WJ8GWosOGcBOSMlvbxOyuaa58K10CUcBu3UNYMXfbifsNunrPPaJClyHK279gtweail/SVEc9XoyUh2uP/sL2eU3edeOBnscRj+0TXCkr+8auzW2BlMHrzDniQBXnFQelm94yDB4Ue3BfqKlkPmfC9QO+X/Jsjl6RtIQ6kqvwz9ZQ9uzFi4ZS5xd0I/IBp621GEXFkqIZhx13ohoiGa1KlhXS1AkDSRptzy7DlkW/ZDR3rh4WKGYsIF7/09WGtSPzicF8E3Mc9BS1OBksTuDuC1ayo6o0qxgEED0s3tNKE4GMQ5Kr9glfvoTbkMtgJst2R+Tq0/HozNCp5Dr4cqTcOZwOTtyPtZbdmzmdfT66mkdC4P1e8JPGugtHv+5HC8u0PSyBQGEewIo7g417mxC/5mf84Cp+1a/TLTe5mb4xqycaUexYM8+jLy4YdStpvR0NuTcx3ZzTwsi357S3NfACfLnU2BnD5WyvID9Y3uNd7Mawa4b+631WfuMdFWBOa3qQYifzdnzo98CpkpatCn5HSse87LqTxZ8Cm8ziu42g/uKcHuvGJXDzcFSD4wk0qclvpcdrSfhRZfU9H/YFK4JuW6q9aAvL24DgdD9mBDI2Gz94xFwu7u9iMivTQXEC5vgHD2lvdH9XtnpHxElqTBN4whFTujk9ZhjcEY50ntoaplQOs0N3S9XhQhLMjNz+g6CvY77jTq+3pdoGffuoecbD/hOWZqebdqaC5Oa7ytnH/YY2J6L3tCTaFloqNOetWvre3uaz5aNh4i6F4f4Z4CkTWGksHPcKdukMQDMNb0gm+JiWt9Wa2bHhQ1KmK+XQHZEIEM6GAhzIDCk0kMeKiyyE3zOwIJomqjHZVb5O4GmpJ1oONZzBSyvdBusaRYl1r8MT5BWgh0f8l34wc5pMxmvZUT11hZPDzawPfKLYCqHDAPvAEOR7JDkOBb3X8ILMRUIGslzzu17y0gP9qiOOuwadoyTcTHxvPJaEc6lQYn3j9E44C3TxDGpNOHl9rgT0EkRT4o2QNLNXXAoKPl2lFw/8goob9s73dMqtzeRwVVJdoC9kGv7TBzDJcs6yIXMM9EvExeuotANi99JdL4xLUZiRrtcv3+fJnIsJPdgczGYV/9ScODFIsa8N3fNFiIOe9b+5gPDwutGVYoSf2Fs+mzsbrxLv7/D+7vRAQxlya9WCvWtw8YjkTaBTEX+y8FUOHtHDBczu4XXEHaznZ1chnV/Pb5FkX78BiEO86ai1dB6v5+F2J/MLdEWhqEvEW8QYAPB6AYwfdEeVZX5igCF17mqbCqS/sg3z/hdW0iLSnjVeYqHXwTHbuFWM/c0MLCddnqvO49vZacWzXHk86wRyghbQTJUx3HUa/mJDGACZysvHLGzv2qjmlsKviokYLWA/Wsg2G1UV3eAmVq7VXt9s3v8nFvTRyqeWeHIkBpw45NFOR/H1sUjWAklBWOdiT/db0y6SOCvaTxVWqnk/6nL+/7vhhF3Zp9HrImt8+lk2vpeAS12YD8mHS/LJPWY+nLBWu1t0yI4aca70gJsBGJI5NvZbDGGe6f5SGfxbVHCr5NS/cBOUMOo+Oh87/IYv2XGZPwvD+7S9b84VnZVXdj9lQh0Et+v+zbaRkB4lquJMoGnF5UwcLtV0/RQybThfIpOYjNMs1LuW/6jPS6PCKlxeUGNwDvlKDCQlDhZkAQ1PPVPMlhA7QlMceaSYXnRLzI+IHJBQb6freSwWF6zBHYFI9H39CxxUUjIiR5sv5ViOi8tO5RgHtF3bsj3i8yQS43SdFTe0C4cxIuBuQMuSz7laE8/gZl3yGo7Wjgy/3RknSjla2sUt0d6PLQ2QQndxcWHKCNt19i6sYTttLqrywCHrjSyYwEBlWEC2nioEDONieaFc3iticbgSD/a5xzW9aefnQVFZWCRWkP0wVtBkf/tGtHl9PXau4+TV66rTq5X9aY3jEeU83OCnsuY5zBHFm3S5blMlvA+ebCcEWhyHDVwLfI7FAQ7fmcz3xO9cQf0waI6CK70yV+poRFAKDiCD1HELm6ij1KR/y2uWPF3LZ5P3pMGTr2W5hnC7Kvh+yqGvaID2L1athALmORgJHv1CDQzx0AzxfGMvUkgtQq8bCdjf0XvX1H4SKiPylc/2AwFM1PwS8eR429YnIMwJ6yyAQ68u5VyEGaFMdyrXX3I/vYStWcuUBrsN4oheWxIcNVKO1fFC0f6O0N5bRf8Kjyz9siRZK3z+vsU/zD1l6ShheQdoDeiXQKYgv91C7X3dJEx7fo60lRHVurySIUq1J5omLBMSl8b8F7Eqw1jTmZlVvSHcgzulhNYbv88MA1reVj6RQOfRA8LfaNID/RA89xAPYuEadd92uAjbQqExObqj5ELcNmhqOX/JrB+hn2aef3pWzSJmy98C9FdJuvF2l2XnZ5sDS25I9hnuCRAHeyHoAk4/VfjqEIjTN8BG1b0sTjSwJFBlHJCwBsKaaJviwXl5qynN1Z2f0IjoZ4gzwl9QDtcZDqYxk+qxbWDFqc9Nz/dhFLjGYTpX8VNEedNaa4/c/wTOpq7KioMEzASNlfwoIk9Ha6Xpph4PDoumFoeE6ONFEHNK3LgPDzEDfjj8HykTkq/VZIA7uN4BaaGFAi5Ah66FuVFFT7Mp9vUMvCfSH/Wdx/AYijS5WNBULlDpLJeLQ79oJfZ1ItrBOfD7oBSt5hIK7qoVM2aCMXwdOVSE6M34SJfgMPC9MCfcJudvQjDvYX31ZA42ZGGDgD4Iab75KMOXMdOR4aqlxCTb6FoLqqahzFTh6bMWtHEdqCz4uTVeHcH/FGUC/2j1BQE5FEKg3EIKl0uja5ztpSZa+3e59cmOHAPcyhlYE9ov+tU6GVzThHqBGs9Wd4zw6oNMvBt+YbbepNJotoU7YtuST1Z5mZIjpJaD+kDHqmmLyDYxlpWaHJFsPETkRsGj1Ko6lzgz1NYVMuejwOw2EwUtWQ+YKy7c3Y54dVk0hWD8oRaqOo8HNQ9YKURUjeIXXZ8OGst6WE5KtvoV/ga8VZQ8gE8SRc/XEWdXGrdDKc8tMxnVN03pSJQGIYquLQGU7QwUUftlJ6MYA8jgQFpGcW0FVBmsxl03zyxSL+FemGrtEoVwAOlfAiLaHuLV2QZ84j+dJAoWjdOFIF+uXwOzyeQNMPTwFg+ivSAkE9D23HFQJOnC34NYENFHBLCZfK+oICubzmXnFKHbLJrlNvlvbCONp+LiFK2iXMda3p8oGojI5o/92vlxF4mn5ZH4JZ8jRX6Xalj18Cw6H6j8UzO6w4cI5OV0RvFEKOwKmGvN/W7OvdHPMBMmg1oxFhAX6p8KdyULTdU+1C4JkX7mBND+Y5mjLv4/M3GIzyEcwyL6sxVTz0aqkKSCltyedUU/g9JmiuEp11d2Pk0skRo1o/gqPdJ5lbVG/fJXGcvxG6sIhAGcCOLIjHG873yWf2rJY7RRWk0W8lX5IjgvmDIWY9Tf8W8dNUWoRTbJ8IzAqL9YtYyOKDt4W8fdXKSRP+cljxQhmHmXTzyJ9mQkGpy0TWsf7VE96ki5aAhbRikjQ5mYN6xLyiLC1YKz8c/tZOb0bPH897bhFgjRHOtfzRbRm9p/Sqk4AMsStChqcVkZDM2bz3WOjNpBLI0/K8OCd+PDT+xg9ehjzBcM2WNjspKmch9rK6T58YRulEmbQbc9QT1+A2lPTnb2aXccptRrP+CwkQ2bsT/OMbUnE3NHvw0/G5Z3pSU0Qy6L10+lJ8SXzFGf4XTPoLuK+fNKj05IWFxJiHINtjo3WbwLdKZp8Kd97X+TW+z7XpP2qKqaRMfeYwzoc56y7w1NnOGzH48qicaC+DXipszT5mYmW6e1bxjwqNpo+E1JmXZQR/LeFi84XEGUYNxaaAciQfRX/6jO4b81TmdvWjyADWHwMonDXXHjO4YoSbh1M6pRKR2Psuw066/O/WvJh+WrIhlsJC6D9W2HtDK8gIU2F8SrpXOxd3hlntVFYsMo37s6kPAtFm2hu+eWN+NtX9/EO8psgAdiXvMNczCSFxV9cdEEXfDHHbQBmH/g+XilwtXYcjoZas9LqLS4xBSU4vLMnCqrtQ0qauh2g52wDFz6mAj7Z6OydXcVDwjK4QD2P2zfAYsfOqIhhg7xEzDJoumTy+eRbkGbFHraOet+LF5N8lKKplstYqiVamzJ56geEsZ6QP1NYv/KXdKk+OjtRC/KdoOZjrBXsoR8/CS8Az6JV/DyGqbTkwlo9gA5hnKaVKtRy9q7uNASl9asjj82WN/0VYIhMfLoR1CbW+vUrOSbAh/ilqbcq3HJ8c5+E2HuhKIbYRJRMQrfJrxKk7xKTRAgeSbA5dRzQACm31Ch2FSvoS5KnIpYXyqm4828lGGNApJiZ1aU0Q2mLUI/ZHAHrzMacshZtckhWG9n9MKIcDc8ld0gl7Tl5QFMOswgHvWyzuEqgTnuBxJqfhTprv6tbLJ4xcpNz2/v4fHE743YuVN5/lJG0SEfwUUq53MtXUqII9PRcjnTAN4TT3Y+cz6QWaW/l/tW0uSvniLha36gw3WlfA5qWdBuwE2uR0XMyYXE5d7WEkHUnAYzzJsRqrvhkqNwQO0V1jF14ApnUltMxqK3pbyWO3tw1o5+PIjcvKFeOFYCUucRa2lS0h4peA8FdiOcMkUabgHdtsAJuUrlxa+Vi39Y6PVg24XRyupLFIy80QmIF5H4GUY/aW5Ig687karDPyHVnr9M6PEJ3q3acV4yKwZSMBvlxqlvUFYgLm8NELucYtQvyI7CYRDl6LvSpdro+FK9IJSaOLXFghk90IlcJz4Ic74UY8a8+l+8tQKEjhVzFeYbLvrHGTv5A0OAs43fxIxAe5q/iqRFZUAXE+h0PUfky5af3xo/7zZXGhnzfz1Um8L58WUa5hTavUhbD0+zRqiajkJ7nyqIKj5b6nRTzHwG+lr7bxs5P5SPYycAoDdEqjT3TUqY3dz2tONxBh1lU7WX3BTxfO/WSqi0Zz+sEAOrvGu8io4SmIM4/Ka5RTXQscbG1I04g28x0fTm6KSXDFhAcNsNw0Mw8o2u5Lxa42szqPdapNqo6lrOP2N6RQ0pzOUJ6d/f/4Yqt1/r6XtoTqCPE70IwyZvaAWjqTX1XoLmvQ4uajXSpxDA84kZ1fFNd96Y892wh4ygX6RMg6vUd67kkRKcrmAckL8UmAklWAJryol61L8fPBt8o32hDuVCE2itZBjVn/4vEZVMARe0U7X/NOb6NDdrbP8TCM4g4+XeV7Jej+MnqvdA0c19fSUqxU1fhrSQL4cuNcdPKbdPSyPzptcmrxngxL9Yotv6/Ah2c4kL8dt1m7uQE8cdunn3qMB176itdJQu6lEF3p/gg/kuBO70VtY6gVISN1cNPnv8EiKYKzLhCf89np5f/fCDWOhciRKDwWRzULf3jZvHDJ8cd25p5JA3UppQIqy/8C9g6UrJwmYWZ8f8z0+sGCY25gGwsQs5UREaq9GQKE//vFneh09XAVdDhzDkqspkWjL6RmLDWdIpttc0N5NTdX2DdqR9JVikvEkg9OBHpYxkxYr0RQMp5bphj0ee6KsYcbZ4x06gbJFeGSgKZjuR4EpDJ8yPJXFFxldxmUtsrCMr/z9NA9olxU7+l4OD+dXd0AEAfMtpIK+g98cuLM0HNfslZ5/YgWBxFF2zmKJFunA9bb/agMyUR9AGAVr/HER9lxJEwdbwKtHQy9vwiMF8G+452JLK/t0/j0mvspENFA9z2ZJPL9jFbSRuF34ppQ3mV/E14IZeGP29NIv2uOHOxL1uzKwNydi1YbLIJRDE25zm0Zh/ulUBJWEnMqUfmzDqo1V8qy4VxfXeym4xccedSOiEyyLna/lP08giG6hUmQLxnvzrMHK2AEe+ggm3RGoS8Rown66LX+See1iohZqnbqxISRf6rNBpM45XPvxph3Q3pKcjDvoeq9eaUFSA92VfxpnVBO/tXtiQGBLYuBSZpqxTWd6G4RP/PFdrmUOhVkRsUXBufoj2MKSgNx6Ab+KMUg5qY52yrkYGKt8O6CubJt0/GJnIlCRODd3ZdcuCNaVWx2PN5+LK6Jy4jEFKtw/f+Ub+piXcpBL02TiO1SCI7qU0MznFxbs5CYrYOo5uQe/CaxWIN8Pq57WuPJ4UT+FHcRW6eiVYxa5hQTAf/ai3XsH5xP2LT/CWdWB175xA8uN8iZ6ai3yJysIENsHcRD6bpGEZ6gc+IIFu+JEhm4zMxIhu9CqjscKnyUIIzasUxdo2Ioa6hZaq4exO0dC/5gQbL4vR23nf5/PfxEzggWNQyDhM+UupTXtBIO2yqV6/wotUPibw5rOcqU+Amua0hAt+7RVXHwvfnFVfCo8E6mdD8wv8xSK2i1mElL9KiW/eVZWBRNE8gSBYQ8DpaNF193CPDyWKxPHz3Hg/aehKa5P4GPE7ugkqhjsYVMUo9Pvq7wXrjAs6U8EKDEh9zsRfFyg91RJaRSqSMNaUbuTkqKYq7KalV3WErKQRL6SxCZa3xGwJ8H5t5bYU+J3lCbtQjtDZ8O66PwKPPvyx1zuiiMjQcwv+JNGNIlFraoEgz5MlGkrBEgIRGXZDuS6ZVAa8L8lqE+/en4pHcj6HNlKtriDeosSDduttT+zi45k4bkgXWgO0a8d7gdqZ3UJR29T2KFIOSrm2Eqat9SF4S8lc33+LUUePfo82x/nQilk3+Eu+HhupyjfM5PGq1dglQ4wG/lJYazW0LlJ774pf/fwHYuPGjVC0CgsOjDzN8o2F6koQV+o90KCDPNkmqpGuAx8+43UGbtxxZGt0OpiFwi3tp8JyYExjdfni19OpI6SI48mM4pZCQXpkhvbZnbvD9hHigF+ERZJfzZZRGExPctz5QL8eQNAFI/KuL4DC23teZmSi1RNxU26PZTrfmtXwgciXXMyvm8j2ACq5n3+x4PyKdvh+SAVEvOQpc1SO7JDZf4+1L+fmB31RWMENRock5xOJ6H2UdMncn+nU9plECElvoLRnImET+mfWHT1fAfrR6iA1Yks9UXu3F2KHIc/wAeChopbz8RCkHQGxOQEVSbujBdd/Hjod1X8g4kF4MsefT+gd4n0UGoUgd30zeGmabC7h7W29PhizM8IZvfZbzVFdLPLU5X8s+wmdN65Woyld4B+BVEQ+w1GA4J4K6WVcSki7AeL4Gv6v3DXYBgdHl5aHXI1epDe0MxtdxvyPhHxoO3pHodmCkdJ1udrGu1167EDHXzknCB5r5U9cxI/UFt54Xxr5LPwrfONSxhMj3DqfGLSQQfxf2cSWWFig3jGf2mOfQ+XzH62mfn3Dv0KSS2M12rilop/c5iFK5ApaiFse2g==
+                </xenc:CipherValue>
+            </xenc:CipherData>
+        </xenc:EncryptedData>
+    </saml2:EncryptedAssertion>
+</saml2p:Response>
diff --git a/nachrichten-manager/src/test/resources/application-bayern.yaml b/nachrichten-manager/src/test/resources/application-bayern.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..765940c40972f684097389caf6331114bfbdf1e0
--- /dev/null
+++ b/nachrichten-manager/src/test/resources/application-bayern.yaml
@@ -0,0 +1,15 @@
+ozgcloud:
+  antragraum:
+    url: https://dev.antragsraum.de/
+    entityId: https://antragsraum.ozgcloud.de/
+    metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+    decryptionPrivateKey: "classpath:/bayernid/bayernid-test-enc.key"
+    decryptionCertificate: "classpath:/bayernid/bayernid-test-enc.crt"
+  nachrichten-manager:
+    url: static://localhost:9091
+grpc:
+  client:
+    info-manager:
+      address: static://localhost:9091
+  server:
+    port: 9092
\ No newline at end of file
diff --git a/nachrichten-manager/src/test/resources/application.yaml b/nachrichten-manager/src/test/resources/application.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/nachrichten-manager/src/test/resources/bayernid-idp-infra.xml b/nachrichten-manager/src/test/resources/bayernid-idp-infra.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ec1ed7ca7099b8be7a8cff7448a740f0b9404c34
--- /dev/null
+++ b/nachrichten-manager/src/test/resources/bayernid-idp-infra.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor entityID="https://infra-pre-id.bayernportal.de/idp">
+        <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+            <md:KeyDescriptor use="signing">
+                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+                    <ds:X509Data>
+                        <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+                            BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD
+                            VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2
+                            MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH
+                            DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq
+                            hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgKWqO2
+                            gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmR
+                            dZjUY2Iui6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+C
+                            Fbo37f9tPwMgf3jgh4gxaujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1
+                            rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFMG7z+Kco2quwOmmZVzWQfeH/3AlN2KbcP
+                            t7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId37MSjoytwBRyd5EACTvhQ
+                            BOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyLdjiy1uCb
+                            jvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGyn
+                            ZmJcZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOE
+                            JFyXIiaqB9J+fcn/biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs
+                            2EG4u1CI2MKDWqK2aEsHrtu8tbS9LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSx
+                            ZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYeltslkaolOmcINXQeSe7nURwp
+                            MB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1UdEwQFMAMBAf8w
+                            DQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM
+                            jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTP
+                            yaHPAZI6e5xHDu8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJ
+                            UyIw5zcKKUQC/JWtMQAQcopbjekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw8
+                            2Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR+Cv3XyzBoGQ/2arijcPnGvth79ff
+                            VFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl92MY+bmugrtTIrazj
+                            tfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq4mJU
+                            Ky/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZh
+                            laZ5ERpWNiv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15
+                            fLXeh2A4YbaBDZdGvDiLOAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpy
+                            LLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ
+                            3emK</ds:X509Certificate>
+                    </ds:X509Data>
+                </ds:KeyInfo>
+            </md:KeyDescriptor>
+            <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml
index 01005831511b2253a348cc33f0ff4f6125a76b6f..6465ac5f120cc4ed9494484bc5e670820f3d9ff9 100644
--- a/src/main/helm/templates/deployment.yaml
+++ b/src/main/helm/templates/deployment.yaml
@@ -180,7 +180,23 @@ spec:
           - name: ozgcloud_bayernid_absender_gemeindeSchluessel
             value: {{ quote (required "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).gemeindeSchluessel) }}
           {{- end }}
-
+          
+          {{- if ((.Values.ozgcloud).antragraum).enabled }}
+          - name: ozgcloud_antragraum_enabled
+            value: {{ quote .Values.ozgcloud.antragraum.enabled }}
+          - name: ozgcloud_antragraum_url
+            value: {{ quote (required "ozgcloud.antragraum.url must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).url) }}
+          - name: ozgcloud_antragraum_metadatauri
+            value: {{ quote (required "ozgcloud.antragraum.metadataUri must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).metadataUri) }}
+          - name: ozgcloud_antragraum_decryptionprivatekey
+            value: {{ quote (required "ozgcloud.antragraum.decryptionPrivateKey must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).decryptionPrivateKey) }}
+          - name: ozgcloud_antragraum_decryptioncertificate
+            value: {{ quote (required "ozgcloud.antragraum.decryptionCertificate must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).decryptionCertificate) }}         
+          {{- end }}
+          {{- if (((.Values.ozgcloud).feature).bescheid).enableDummyDocumentProcessor }}
+          - name: ozgcloud_feature_bescheid_enableDummyDocumentProcessor
+            value: {{ quote (((.Values.ozgcloud).feature).bescheid).enableDummyDocumentProcessor }}
+          {{- end }}
 
           {{- if (.Values.ozgcloud).processors}}
           {{- range $processor_index, $processor := (.Values.ozgcloud).processors }}
diff --git a/src/main/helm/templates/network_policy.yaml b/src/main/helm/templates/network_policy.yaml
index fb20373df7e5cf5c1045657b25fb3df4057f48cc..88ad059ed4898240276aa3f9a0dc05775705a87e 100644
--- a/src/main/helm/templates/network_policy.yaml
+++ b/src/main/helm/templates/network_policy.yaml
@@ -47,6 +47,9 @@ spec:
           ozg-component: eingangsadapter
 {{- with (.Values.networkPolicy).additionalIngressConfig }}
 {{ toYaml . | indent 2 }}
+{{- end }}
+{{- with (.Values.networkPolicy).additionalIngressConfigNamespace }}
+{{ toYaml . | indent 2 }}
 {{- end }}
   egress:
   - to:
@@ -101,5 +104,8 @@ spec:
 {{- with (.Values.networkPolicy).additionalEgressConfig }}
 {{ toYaml . | indent 2 }}
 {{- end }}
+{{- with (.Values.networkPolicy).additionalEgressConfigNamespace }}
+{{ toYaml . | indent 2 }}
+{{- end }}
 
 {{- end }}
\ No newline at end of file
diff --git a/src/test/helm/deployment_antragraum_test.yaml b/src/test/helm/deployment_antragraum_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9d8cc1af7e263702ba1e354064c5459800a00269
--- /dev/null
+++ b/src/test/helm/deployment_antragraum_test.yaml
@@ -0,0 +1,124 @@
+#
+# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+# Ministerpräsidenten des Landes Schleswig-Holstein
+# Staatskanzlei
+# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+#
+# Lizenziert unter der EUPL, Version 1.2 oder - sobald
+# diese von der Europäischen Kommission genehmigt wurden -
+# Folgeversionen der EUPL ("Lizenz");
+# Sie dürfen dieses Werk ausschließlich gemäß
+# dieser Lizenz nutzen.
+# Eine Kopie der Lizenz finden Sie hier:
+#
+# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+#
+# Sofern nicht durch anwendbare Rechtsvorschriften
+# gefordert oder in schriftlicher Form vereinbart, wird
+# die unter der Lizenz verbreitete Software "so wie sie
+# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+# ausdrücklich oder stillschweigend - verbreitet.
+# Die sprachspezifischen Genehmigungen und Beschränkungen
+# unter der Lizenz sind dem Lizenztext zu entnehmen.
+#
+
+suite: deployment antragraum
+release:
+  name: vorgang-manager
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  imagePullSecret: image-pull-secret
+  ozgcloud:
+    environment: dev
+    antragraum:
+      enabled: true
+      url: https://antragraum.address
+      metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+      decryptionPrivateKey: "decryptionPrivateKey_secret"
+      decryptionCertificate: "decryptionCertificate_secret"
+tests:
+  - it: should enable antragraum
+    templates:
+      - templates/deployment.yaml
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_antragraum_enabled
+            value: "true"
+  - it: should fail if antragraum url is not set
+    set:
+      ozgcloud:
+        environment: dev
+        antragraum:
+          enabled: true
+          url: 
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.antragraum.url must be set if ozgcloud.antragraum is enabled"
+
+  - it: should set metadataUri
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_antragraum_metadatauri
+            value: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+  - it: should fail if metadataUri is not set
+    set:
+      ozgcloud:
+        antragraum:
+          metadataUri:
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.antragraum.metadataUri must be set if ozgcloud.antragraum is enabled"
+          
+  - it: should set metadataUri
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_antragraum_metadatauri
+            value: "classpath:/bayernid/metadata/bayernid-idp-infra.xml"
+  - it: should fail if metadataUri is not set
+    set:
+      ozgcloud:
+        antragraum:
+          metadataUri:
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.antragraum.metadataUri must be set if ozgcloud.antragraum is enabled"
+
+  - it: should set decryptionPrivateKey
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_antragraum_decryptionprivatekey
+            value: "decryptionPrivateKey_secret"
+  - it: should fail if decryptionPrivateKey is not set
+    set:
+      ozgcloud:
+        antragraum:
+          decryptionPrivateKey:
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.antragraum.decryptionPrivateKey must be set if ozgcloud.antragraum is enabled"
+
+  - it: should set decryptionPrivateKey
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_antragraum_decryptioncertificate
+            value: "decryptionCertificate_secret"
+  - it: should fail if decryptionCertificate is not set
+    set:
+      ozgcloud:
+        antragraum:
+          decryptionCertificate:
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.antragraum.decryptionCertificate must be set if ozgcloud.antragraum is enabled"
\ No newline at end of file
diff --git a/src/test/helm/deployment_dummy_bescheid_document_test.yaml b/src/test/helm/deployment_dummy_bescheid_document_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8863f522e03eb89a1967528f116e5195ac42a473
--- /dev/null
+++ b/src/test/helm/deployment_dummy_bescheid_document_test.yaml
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+# Ministerpräsidenten des Landes Schleswig-Holstein
+# Staatskanzlei
+# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+#
+# Lizenziert unter der EUPL, Version 1.2 oder - sobald
+# diese von der Europäischen Kommission genehmigt wurden -
+# Folgeversionen der EUPL ("Lizenz");
+# Sie dürfen dieses Werk ausschließlich gemäß
+# dieser Lizenz nutzen.
+# Eine Kopie der Lizenz finden Sie hier:
+#
+# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+#
+# Sofern nicht durch anwendbare Rechtsvorschriften
+# gefordert oder in schriftlicher Form vereinbart, wird
+# die unter der Lizenz verbreitete Software "so wie sie
+# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+# ausdrücklich oder stillschweigend - verbreitet.
+# Die sprachspezifischen Genehmigungen und Beschränkungen
+# unter der Lizenz sind dem Lizenztext zu entnehmen.
+#
+
+suite: deployment dummy bescheid document processor
+release:
+  name: vorgang-manager
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  imagePullSecret: image-pull-secret
+  ozgcloud:
+    environment: dev
+    feature:
+      bescheid:
+        enableDummyDocumentProcessor: true
+tests:
+  - it: should enable dummy bescheid document processor
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_feature_bescheid_enableDummyDocumentProcessor
+            value: "true"
diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml
index 16661a34b0b43473c82029e7496f2867220dd785..ac7df6574a59eae14b64a88872f8f37a182dfa89 100644
--- a/src/test/helm/network_policy_test.yaml
+++ b/src/test/helm/network_policy_test.yaml
@@ -253,39 +253,78 @@ tests:
                 matchLabels:
                   component: client2
 
-  - it: add egress rules by values
+  - it: should add additionalIngressConfig
     set:
       networkPolicy:
+        dnsServerNamespace: test-namespace-dns
+        additionalIngressConfig:
+        - from:
+          - podSelector: 
+              matchLabels:
+                additionalIngressConfig: yes
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+            - podSelector: 
+                matchLabels:
+                  additionalIngressConfig: yes
+
+  - it: should add additionalEgressConfig
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-namespace-dns
         additionalEgressConfig:
-        - to:
-          - ipBlock:
-              cidr: 1.2.3.4/32
         - to:
           - podSelector:
               matchLabels:
-                component: ozg-testservice
-          ports:
-            - port: 12345
-              protocol: TCP
-
-        dnsServerNamespace: test-dns-namespace
+                additionalEgressConfig: yes
     asserts:
     - contains:
         path: spec.egress
         content:
           to:
-          - ipBlock:
-              cidr: 1.2.3.4/32
+          - podSelector:
+              matchLabels:
+                additionalEgressConfig: yes
+
+
+  - it: should add additionalIngressConfigNamespace
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-namespace-dns
+        additionalIngressConfigNamespace:
+        - from:
+          - podSelector: 
+              matchLabels:
+                additionalIngressConfigNamespace: yes
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+            - podSelector: 
+                matchLabels:
+                  additionalIngressConfigNamespace: yes
+
+  - it: should add additionalEgressConfigNamespace
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-namespace
+        additionalEgressConfigNamespace:
+        - to:
+          - podSelector:
+              matchLabels:
+                additionalEgressConfigNamespace: yes
+    asserts:
     - contains:
         path: spec.egress
         content:
           to:
           - podSelector:
               matchLabels:
-                component: ozg-testservice
-          ports:
-            - port: 12345
-              protocol: TCP
+                additionalEgressConfigNamespace: yes
 
   - it: test network policy disabled
     set:
diff --git a/vorgang-manager-interface/src/main/protobuf/antragraum.model.proto b/vorgang-manager-interface/src/main/protobuf/antragraum.model.proto
index 16eb419ccb78e483b10cdf075337b069008fa1cc..062c92b7c6e0d9cdc7c2f40902737f61bed20094 100644
--- a/vorgang-manager-interface/src/main/protobuf/antragraum.model.proto
+++ b/vorgang-manager-interface/src/main/protobuf/antragraum.model.proto
@@ -32,42 +32,33 @@ option java_package = "de.ozgcloud.nachrichten.antragraum";
 option java_outer_classname = "AntragraumModelProto";
 
 message GrpcFindRueckfragenRequest {
-	string samlToken = 1;
-	string postfachId = 2;
+  string samlToken = 1;
+  string postfachId = 2;
 }
 
 message GrpcFindRueckfragenResponse {
-	repeated GrpcRueckfrage rueckfrage = 1;
-}
-
-message GrpcGetRueckfrageRequest {
-	string samlToken = 1;
-	string rueckfrageId = 2;
-}
-
-message GrpcGetRueckfrageResponse {
-	GrpcRueckfrage rueckfrage = 1;
+  repeated GrpcRueckfrage rueckfrage = 1;
 }
 
 message GrpcRueckfrage {
-	string id = 1;
-	string vorgangId = 2;
-	string vorgangName = 3;
-	string sendAt = 4;
-	string answeredAt = 5;
-	string status = 6;
-	string text = 7;
-	repeated string answerAttachmentFileId = 8;
-	repeated GrpcRueckfrageAnswer answers = 9;
+  string id = 1;
+  string vorgangId = 2;
+  string vorgangName = 3;
+  string sentAt = 4;
+  string answeredAt = 5;
+  string status = 6;
+  string text = 7;
+  repeated string attachmentFileId = 8;
+  repeated GrpcRueckfrageAnswer answers = 9;
 }
 
 message GrpcSendRueckfrageAnswerRequest {
-	string samlToken = 1;
-	GrpcRueckfrageAnswer answer = 2;
+  string samlToken = 1;
+  GrpcRueckfrageAnswer answer = 2;
 }
 
 message GrpcRueckfrageAnswer {
-	string rueckfrageId = 1;
-	string answerText = 2;
-	repeated string answerAttachmentFileId = 3;
+  string rueckfrageId = 1;
+  string answerText = 2;
+  repeated string attachmentFileId = 3;
 }
\ No newline at end of file
diff --git a/vorgang-manager-interface/src/main/protobuf/antragraum.proto b/vorgang-manager-interface/src/main/protobuf/antragraum.proto
index d5874e2a11806972c3ee66107ae2b616aefe878e..0fa6d67dffed44a28d58a531d8ff1ad03bf128ea 100644
--- a/vorgang-manager-interface/src/main/protobuf/antragraum.proto
+++ b/vorgang-manager-interface/src/main/protobuf/antragraum.proto
@@ -33,12 +33,9 @@ option java_package = "de.ozgcloud.nachrichten.antragraum";
 option java_outer_classname = "AntragraumProto";
 
 service AntragraumService {
-	rpc FindRueckfragen(GrpcFindRueckfragenRequest) returns (GrpcFindRueckfragenResponse) {
-	}
+  rpc FindRueckfragen(GrpcFindRueckfragenRequest) returns (GrpcFindRueckfragenResponse) {
+  }
 
-	rpc GetRueckfrage(GrpcGetRueckfrageRequest) returns (GrpcGetRueckfrageResponse) {
-	}
-
-	rpc SendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest) returns (de.ozgcloud.vorgang.grpc.command.GrpcCommand) {
-	}
+  rpc SendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest) returns (de.ozgcloud.vorgang.grpc.command.GrpcCommand) {
+  }
 }
\ No newline at end of file
diff --git a/vorgang-manager-interface/src/main/protobuf/document.model.proto b/vorgang-manager-interface/src/main/protobuf/document.model.proto
new file mode 100644
index 0000000000000000000000000000000000000000..ff0025a2197b3ba328547cd595f741ea95852310
--- /dev/null
+++ b/vorgang-manager-interface/src/main/protobuf/document.model.proto
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+ syntax = "proto3";
+
+ package de.ozgcloud.document;
+
+ option java_multiple_files = true;
+ option java_package = "de.ozgcloud.document";
+ option java_outer_classname = "DocumentModelProto";
+
+message GrpcGetDocumentRequest {
+	string id = 1;
+}
+
+message GrpcGetDocumentResponse {
+	GrpcDocument document = 1;
+}
+
+message GrpcDocument {
+	string id = 1;
+	string type = 2;
+	string fileId = 3;
+	string nachrichtSubject = 4;
+	string nachrichtText = 5;
+}
\ No newline at end of file
diff --git a/vorgang-manager-interface/src/main/protobuf/document.proto b/vorgang-manager-interface/src/main/protobuf/document.proto
new file mode 100644
index 0000000000000000000000000000000000000000..c2a903373b229e3461932fa15e50706a9e5b41af
--- /dev/null
+++ b/vorgang-manager-interface/src/main/protobuf/document.proto
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+syntax = "proto3";
+
+package de.ozgcloud.document;
+
+import "document.model.proto";
+
+option java_multiple_files = true;
+option java_package = "de.ozgcloud.document";
+option java_outer_classname = "DocumentProto";
+
+service DocumentService {
+  rpc GetDocument(GrpcGetDocumentRequest) returns (GrpcGetDocumentResponse) {}
+}
\ No newline at end of file
diff --git a/vorgang-manager-server/pom.xml b/vorgang-manager-server/pom.xml
index 06f4f37af8f931a634649d52ed1213713bc83e4b..cdc3e660057ab9c6802a58676b672a5077e2cfef 100644
--- a/vorgang-manager-server/pom.xml
+++ b/vorgang-manager-server/pom.xml
@@ -54,7 +54,7 @@
 
 		<user-manager-interface.version>2.1.0</user-manager-interface.version>
 		<bescheid-manager.version>1.10.0-SNAPSHOT</bescheid-manager.version>
-		<processor-manager.version>0.3.0</processor-manager.version>
+		<processor-manager.version>0.4.0-SNAPSHOT</processor-manager.version>
 		<ozgcloud-starter.version>0.7.0-SNAPSHOT</ozgcloud-starter.version>
 
 		<zip.version>2.11.1</zip.version>
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItem.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItem.java
index 7b96198ef4a1f2d0f062f32a5b102294721625a9..3757c40d46163e6fdb4a209873fb3c5b7bb0220c 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItem.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItem.java
@@ -25,12 +25,13 @@ package de.ozgcloud.vorgang.attached_item;
 
 import java.util.Map;
 
+import jakarta.validation.constraints.NotBlank;
+
 import org.springframework.data.annotation.Id;
 import org.springframework.data.annotation.TypeAlias;
 import org.springframework.data.annotation.Version;
 import org.springframework.data.mongodb.core.mapping.Document;
 
-import jakarta.validation.constraints.NotBlank;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
@@ -50,9 +51,9 @@ public class VorgangAttachedItem {
 	static final String FIELDNAME_ID = "id";
 	static final String FIELDNAME_VERSION = "version";
 	static final String FIELDNAME_CLIENT = "client";
-	static final String FIELDNAME_VORGANG_ID = "vorgangId";
-	static final String FIELDNAME_ITEM_NAME = "itemName";
-	static final String FIELDNAME_ITEM = "item";
+	public static final String FIELDNAME_VORGANG_ID = "vorgangId";
+	public static final String FIELDNAME_ITEM_NAME = "itemName";
+	public static final String FIELDNAME_ITEM = "item";
 	public static final String FIELDNAME_IS_DELETED = "deleted";
 
 	@Id
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java
index 2c981d73c499dcd683a2e456c538b4197650b9cf..63aee3c41f4aaa594572b35da58cd5270575336e 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java
@@ -1,9 +1,5 @@
 /*
- * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
+ * Copyright (c) 2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -256,7 +252,7 @@ class PersistPostfachNachrichtByCommandService implements PersistPostfachNachric
 
 	// uses map file: src/main/resources/mime.types
 	private String getByMimeTypes(String fileName) {
-		MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
+		var fileTypeMap = new MimetypesFileTypeMap();
 
 		return fileTypeMap.getContentType(fileName);
 	}
@@ -268,10 +264,13 @@ class PersistPostfachNachrichtByCommandService implements PersistPostfachNachric
 		return Stream.of(PostfachNachricht.builder()
 				.attachments(Collections.emptyList())
 				.createdAt(ZonedDateTime.now())
+				.sentAt(ZonedDateTime.now())
+				.vorgangId("VORGANG_DUMMY_URI")
 				.createdBy("Klaus")
 				.direction(Direction.OUT)
 				.replyOption(ReplyOption.POSSIBLE)
 				.mailBody("Lorem ipsum dolres est")
+				.id(UUID.randomUUID().toString())
 				.subject("Test")
 				.build());
 	}
diff --git a/vorgang-manager-server/src/main/resources/application-local.yml b/vorgang-manager-server/src/main/resources/application-local.yml
index 346ab37983c1d9a1e395e8ba52c0d31823caca68..f575ac6f04eaec08c1ea13a5f28d849eebe0576c 100644
--- a/vorgang-manager-server/src/main/resources/application-local.yml
+++ b/vorgang-manager-server/src/main/resources/application-local.yml
@@ -53,6 +53,9 @@ ozgcloud:
   elasticsearch:
     initEnabled: true
     index: test-index
+  feature:
+    bescheid:
+      enable-dummy-document-processor: true
 
 
 mongock:
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidDocumentITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidDocumentITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..54cfc7b0679835e54404911bcb0c8945533ed8d7
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidDocumentITCase.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid;
+
+import static de.ozgcloud.bescheid.BescheidEventListener.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.test.annotation.DirtiesContext;
+
+import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.command.Command;
+import de.ozgcloud.command.CommandCreatedEvent;
+import de.ozgcloud.command.CommandFailedEvent;
+import de.ozgcloud.common.test.ITCase;
+import de.ozgcloud.vorgang.VorgangManagerServerApplication;
+import de.ozgcloud.vorgang.command.CommandTestFactory;
+
+class BescheidDocumentITCase {
+
+	@Nested
+	@SpringBootTest(classes = VorgangManagerServerApplication.class)
+	@ITCase
+	@DirtiesContext
+	class TestDocumentProzessorNotConfigured {
+
+		@Autowired
+		private ApplicationEventPublisher eventPublisher;
+		@SpyBean
+		private BescheidEventListener eventListener;
+		@SpyBean
+		private TestEventListener testEventListener;
+
+		@MockBean
+		private CurrentUserService currentUserService;
+
+		@Captor
+		private ArgumentCaptor<CommandFailedEvent> commandFailedEventCaptor;
+
+		@Test
+		void shouldFailWhenNoDocumentProcessor() {
+			var command = buildCreateDocumentCommand();
+
+			eventPublisher.publishEvent(new CommandCreatedEvent(command));
+
+			Awaitility.await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(testEventListener).onCommandFailed(commandFailedEventCaptor.capture());
+				assertThat(commandFailedEventCaptor.getValue().getSource()).isEqualTo(command.getId());
+				assertThat(commandFailedEventCaptor.getValue().getErrorMessage()).isNotEmpty();
+			});
+
+		}
+
+		private Command buildCreateDocumentCommand() {
+			return CommandTestFactory.createBuilder()
+					.vorgangId("vorgang-id")
+					.relationId("bescheidItem-id")
+					.order(BescheidEventListener.CREATE_BESCHEID_DOCUMENT_ORDER)
+					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
+							GENEHMIGT_BODYKEY, true))
+					.build();
+		}
+
+	}
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7a2ad373088f30562bdd8876f6931a6492fda7a
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid;
+
+import static de.ozgcloud.bescheid.BescheidEventListener.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.awaitility.Awaitility.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.gridfs.GridFsTemplate;
+import org.springframework.test.annotation.DirtiesContext;
+
+import com.mongodb.client.gridfs.model.GridFSFile;
+
+import de.ozgcloud.bescheid.attacheditem.AttachedItemService;
+import de.ozgcloud.bescheid.common.callcontext.CallContextUser;
+import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.bescheid.common.callcontext.UserProfile;
+import de.ozgcloud.command.Command;
+import de.ozgcloud.command.CommandCreatedEvent;
+import de.ozgcloud.command.CommandStatus;
+import de.ozgcloud.common.test.DataITCase;
+import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.document.Document;
+import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
+import de.ozgcloud.vorgang.VorgangManagerServerApplication;
+import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem;
+import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory;
+import de.ozgcloud.vorgang.command.CommandService;
+import de.ozgcloud.vorgang.command.CommandTestFactory;
+import de.ozgcloud.vorgang.command.CreateCommandRequest;
+import de.ozgcloud.vorgang.status.StatusChangedEvent;
+import de.ozgcloud.vorgang.vorgang.Vorgang;
+import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
+
+@SpringBootTest(classes = VorgangManagerServerApplication.class, properties = {
+		"grpc.server.inProcessName=test",
+		"grpc.server.port=-1",
+		"grpc.client.ozgcloud-command-manager.address=in-process:test",
+		"grpc.client.vorgang-manager.address=in-process:test",
+		"grpc.client.nachrichten-manager.address=in-process:test",
+		"grpc.client.pluto.address=in-process:test",
+		"ozgcloud.feature.bescheid.enable-dummy-document-processor=true",
+})
+@DataITCase
+@DirtiesContext
+class BescheidEventListenerITCase {
+
+	@Autowired
+	private CommandService commandService;
+	@Autowired
+	private ApplicationEventPublisher eventPublisher;
+	@SpyBean
+	private TestEventListener bescheiTestEventListener;
+
+	@Autowired
+	private MongoOperations mongoOperations;
+	@Autowired
+	private GridFsTemplate gridFsTemplate;
+
+	@MockBean
+	private CurrentUserService currentUserService;
+	@Mock
+	private CallContextUser callContextUser;
+	@Mock
+	private UserProfile userProfile;
+
+	@Captor
+	private ArgumentCaptor<BescheidDocumentCreatedEvent> bescheidDocumentCreatedEventCaptor;
+
+	@BeforeEach
+	void init() {
+		mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME);
+		mongoOperations.dropCollection(Vorgang.COLLECTION_NAME);
+		when(currentUserService.getUser()).thenReturn(callContextUser);
+		when(currentUserService.getUserProfile()).thenReturn(userProfile);
+	}
+
+	@Nested
+	class TestCreateBescheidDocument {
+
+		@Test
+		void shouldCreateBescheidDocument() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId();
+
+			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture());
+				assertThat(getBescheidDocument()).isNotNull();
+			});
+		}
+
+		@Test
+		void shouldCreateDocumentFile() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId();
+
+			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture());
+				assertThat(getBescheidDocumentFile()).isNotNull();
+			});
+		}
+
+		private VorgangAttachedItem createBescheidAttachedItem(String vorgangId) {
+			return VorgangAttachedItemTestFactory.createBuilder()
+					.id(null)
+					.version(0L)
+					.vorgangId(vorgangId)
+					.itemName(AttachedItemService.BESCHEID_ITEM_NAME)
+					.item(Map.of(Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name()))
+					.build();
+		}
+
+		private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, String bescheidItemId) {
+			var command = CommandTestFactory.createBuilder()
+					.vorgangId(vorgangId)
+					.relationId(bescheidItemId)
+					.order(BescheidEventListener.CREATE_BESCHEID_DOCUMENT_ORDER)
+					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
+							GENEHMIGT_BODYKEY, true))
+					.build();
+			return new CommandCreatedEvent(command);
+		}
+
+		private GridFSFile getBescheidDocumentFile() {
+			var fileId = getBescheidDocument().getItem().get(Document.FIELD_DOCUMENT_FILE).toString();
+			return gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
+		}
+
+		private VorgangAttachedItem getBescheidDocument() {
+			var documentId = bescheidDocumentCreatedEventCaptor.getValue().getCreatedResource();
+			return mongoOperations.findById(documentId, VorgangAttachedItem.class);
+		}
+
+	}
+
+	@Nested
+	class TestSendBescheidManually {
+
+		@Captor
+		private ArgumentCaptor<BescheidSentEvent> bescheidSentEventCaptor;
+		@Captor
+		private ArgumentCaptor<StatusChangedEvent> statusChangedEventCaptor;
+
+		@Test
+		void shouldSetBescheidStatusToSent() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItem));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture());
+				assertThat(loadBescheid(bescheidItem.getId()).getItem()).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SEND.name());
+			});
+		}
+
+		@Test
+		void shouldSetVorgangStatusToBeschieden() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+			var event = buildCommandCreatedEvent(vorgangId, bescheidItem);
+
+			eventPublisher.publishEvent(event);
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidStatusChangedEvent(statusChangedEventCaptor.capture());
+				assertThat(loadVorgang(vorgangId).getStatus()).isEqualTo(Vorgang.Status.BESCHIEDEN);
+			});
+		}
+
+		private VorgangAttachedItem createBescheidAttachedItemWithDocument(String vorgangId) {
+			return VorgangAttachedItemTestFactory.createBuilder()
+					.id(null)
+					.version(0L)
+					.vorgangId(vorgangId)
+					.itemName(AttachedItemService.BESCHEID_ITEM_NAME)
+					.item(Map.of(
+							Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name(),
+							Bescheid.FIELD_SEND_BY, Bescheid.SendBy.MANUAL.name(),
+							Bescheid.FIELD_BESCHEID_DOCUMENT, "documentId"))
+					.build();
+		}
+
+		private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, VorgangAttachedItem bescheidItem) {
+			var command = CommandTestFactory.createBuilder()
+					.vorgangId(vorgangId)
+					.relationId(bescheidItem.getId())
+					.relationVersion(bescheidItem.getVersion())
+					.order(BescheidEventListener.SEND_BESCHEID_ORDER)
+					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
+							GENEHMIGT_BODYKEY, true))
+					.build();
+			return new CommandCreatedEvent(command);
+		}
+
+		private VorgangAttachedItem loadBescheid(String bescheidId) {
+			return mongoOperations.findById(bescheidId, VorgangAttachedItem.class);
+		}
+
+		private Vorgang loadVorgang(String vorgangId) {
+			return mongoOperations.findById(vorgangId, Vorgang.class);
+		}
+	}
+
+	@Nested
+	class TestSendBescheidPostfachMail {
+
+		private static final String NACHRICHT_SUBJECT = "nachrichtSubject";
+		private static final String NACHRICHT_TEXT = "nachrichtText";
+
+		@Captor
+		private ArgumentCaptor<BescheidSentEvent> bescheidSentEventCaptor;
+		@Captor
+		private ArgumentCaptor<StatusChangedEvent> statusChangedEventCaptor;
+
+		@BeforeEach
+		void init() {
+			when(userProfile.getId()).thenReturn(UserId.from("user-id"));
+		}
+
+		@Test
+		void shouldSuccessfullyCompleteCommand() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			commandService.createCommand(buildCreateCommandRequest(vorgangId, bescheidItem));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture());
+				assertThat(loadCommand(bescheidSentEventCaptor.getValue().getSource()).getStatus()).isEqualTo(CommandStatus.FINISHED);
+			});
+		}
+
+		@Test
+		void shouldSetBescheidStatusToSent() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItem));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture());
+				assertThat(loadBescheid(bescheidItem.getId()).getItem()).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SEND.name());
+			});
+		}
+
+		@Test
+		void shouldSetVorgangStatusToBeschieden() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			commandService.createCommand(buildCreateCommandRequest(vorgangId, bescheidItem));
+
+			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidStatusChangedEvent(statusChangedEventCaptor.capture());
+				assertThat(loadCommand(statusChangedEventCaptor.getValue().getSource()).getStatus()).isEqualTo(CommandStatus.FINISHED);
+				assertThat(loadVorgang(vorgangId).getStatus()).isEqualTo(Vorgang.Status.BESCHIEDEN);
+			});
+		}
+
+		@Test
+		void shouldSaveNachrichtDraft() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			commandService.createCommand(buildCreateCommandRequest(vorgangId, bescheidItem));
+
+			await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture());
+				assertThat(loadCommand(bescheidSentEventCaptor.getValue().getSource()).getStatus()).isEqualTo(CommandStatus.FINISHED);
+				var nachrichtDraft = loadNachricht(vorgangId);
+				assertThat(nachrichtDraft.getItem()).containsEntry(PostfachNachricht.FIELD_SUBJECT, NACHRICHT_SUBJECT);
+				assertThat(nachrichtDraft.getItem()).containsEntry(PostfachNachricht.FIELD_MAIL_BODY, NACHRICHT_TEXT);
+			});
+		}
+
+		@Test
+		void shouldSetClientAttribute() {
+			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
+			var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId));
+
+			commandService.createCommand(buildCreateCommandRequest(vorgangId, bescheidItem));
+
+			await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
+				verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture());
+				assertThat(loadCommand(bescheidSentEventCaptor.getValue().getSource()).getStatus()).isEqualTo(CommandStatus.FINISHED);
+				var vorgang = loadVorgang(vorgangId);
+				assertThat(vorgang.getClientAttributes()).containsKey(BescheidCallContextAttachingInterceptor.BESCHEID_MANAGER_CLIENT_NAME);
+
+			});
+		}
+
+		private VorgangAttachedItem createBescheidAttachedItemWithDocument(String vorgangId) {
+			return VorgangAttachedItemTestFactory.createBuilder()
+					.id(null)
+					.version(0L)
+					.vorgangId(vorgangId)
+					.itemName(AttachedItemService.BESCHEID_ITEM_NAME)
+					.item(Map.of(
+							Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name(),
+							Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name(),
+							Bescheid.FIELD_BEWILLIGT, true,
+							Bescheid.FIELD_BESCHEID_DOCUMENT, "documentId",
+							Bescheid.FIELD_NACHRICHT_SUBJECT, NACHRICHT_SUBJECT,
+							Bescheid.FIELD_NACHRICHT_TEXT, NACHRICHT_TEXT))
+					.build();
+		}
+
+		private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, VorgangAttachedItem bescheidItem) {
+			var command = CommandTestFactory.createBuilder()
+					.vorgangId(vorgangId)
+					.relationId(bescheidItem.getId())
+					.relationVersion(bescheidItem.getVersion())
+					.order(SEND_POSTFACH_MAIL_ORDER)
+					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
+							GENEHMIGT_BODYKEY, true))
+					.build();
+			return new CommandCreatedEvent(command);
+		}
+
+		private CreateCommandRequest buildCreateCommandRequest(String vorgangId, VorgangAttachedItem bescheidItem) {
+			return CreateCommandRequest.builder()
+					.vorgangId(vorgangId)
+					.relationId(bescheidItem.getId())
+					.relationVersion(bescheidItem.getVersion())
+					.order(SEND_POSTFACH_MAIL_ORDER)
+					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
+							GENEHMIGT_BODYKEY, true))
+					.build();
+		}
+
+		private Command loadCommand(String commandId) {
+			return mongoOperations.findById(commandId, Command.class);
+		}
+
+		private VorgangAttachedItem loadBescheid(String bescheidId) {
+			return mongoOperations.findById(bescheidId, VorgangAttachedItem.class);
+		}
+
+		private Vorgang loadVorgang(String vorgangId) {
+			return mongoOperations.findById(vorgangId, Vorgang.class);
+		}
+
+		private VorgangAttachedItem loadNachricht(String vorgangId) {
+			var criteria = new Criteria().andOperator(
+					Criteria.where(VorgangAttachedItem.FIELDNAME_VORGANG_ID).is(vorgangId),
+					Criteria.where(VorgangAttachedItem.FIELDNAME_ITEM_NAME).is("PostfachMail")
+			);
+			var vorgangAttachedItems = mongoOperations.find(Query.query(criteria), VorgangAttachedItem.class);
+			assertThat(vorgangAttachedItems).hasSize(1);
+			return vorgangAttachedItems.getFirst();
+		}
+	}
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/TestEventListener.java b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/TestEventListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..1968b51b907dede97a4580038ac49b352729283d
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/TestEventListener.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.bescheid;
+
+import org.springframework.context.event.EventListener;
+
+import de.ozgcloud.command.CommandFailedEvent;
+import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.vorgang.status.StatusChangedEvent;
+
+class TestEventListener {
+
+	@EventListener
+	public void onBescheidDocumentCreated(BescheidDocumentCreatedEvent event) {
+	}
+
+	@EventListener
+	public void onCommandFailed(CommandFailedEvent event) {
+	}
+
+	@EventListener
+	public void onBescheidSentEvent(BescheidSentEvent event) {
+	}
+
+	@EventListener
+	public void onBescheidStatusChangedEvent(StatusChangedEvent event) {
+	}
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/document/DocumentGrpcServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/document/DocumentGrpcServiceITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..95720de7ca399b35508f54d47da2550cb858dadd
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/document/DocumentGrpcServiceITCase.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.document;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.test.annotation.DirtiesContext;
+
+import de.ozgcloud.bescheid.common.callcontext.CallContextUser;
+import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.common.test.DataITCase;
+import de.ozgcloud.document.DocumentServiceGrpc.DocumentServiceBlockingStub;
+import de.ozgcloud.vorgang.VorgangManagerServerApplication;
+import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem;
+import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory;
+import io.grpc.StatusRuntimeException;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+
+@SpringBootTest(classes = VorgangManagerServerApplication.class, properties = {
+		"grpc.server.inProcessName=test",
+		"grpc.server.port=-1",
+		"grpc.client.vorgang-manager.address=in-process:test",
+		"grpc.client.inProcess.address=in-process:test",
+})
+@DataITCase
+@DirtiesContext
+class DocumentGrpcServiceITCase {
+
+	@GrpcClient("inProcess")
+	private DocumentServiceBlockingStub documentServiceStub;
+
+	@Autowired
+	private MongoOperations mongoOperations;
+
+	@MockBean
+	private CurrentUserService currentUserService;
+	@Mock
+	private CallContextUser callContextUser;
+
+	@BeforeEach
+	void init() {
+		mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME);
+		when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser));
+		when(currentUserService.getUser()).thenReturn(callContextUser);
+	}
+
+	@Test
+	void shouldGetDocument() {
+		var documentId = saveDocument();
+
+		var response = documentServiceStub.getDocument(GrpcGetDocumentRequest.newBuilder().setId(documentId).build());
+
+		assertThat(response.hasDocument()).isTrue();
+	}
+
+	@Test
+	void shouldThrowExceptionWhenDocumentNotFound() {
+		saveDocument();
+		var request = GrpcGetDocumentRequest.newBuilder().setId("not-found").build();
+
+		Assertions.assertThrows(StatusRuntimeException.class, () -> documentServiceStub.getDocument(request));
+	}
+
+	private String saveDocument() {
+		var document  = VorgangAttachedItemTestFactory.createBuilder()
+				.id(null)
+				.version(0)
+				.itemName(DocumentService.DOCUMENT_ITEM_NAME)
+				.item(Map.of(
+								Document.FIELD_DOCUMENT_TYPE, DocumentService.DOCUMENT_TYPE,
+								Document.FIELD_DOCUMENT_FILE, "file-id",
+								Document.FIELD_NACHRICHT_TEXT, "nachricht-text",
+								Document.FIELD_NACHRICHT_SUBJECT, "nachricht-subject"))
+				.build();
+		return mongoOperations.save(document, VorgangAttachedItem.COLLECTION_NAME).getId();
+	}
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java
index 055a48e234bab978031fcbfa3d91176df3c20640..8db9ce23a49f33ee2b2a058eb33906fd56882813 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java
@@ -150,7 +150,7 @@ class PostfachMailITCase {
 				assertThat(mails.get(0).getCreatedAt()).isNotNull();
 				assertThat(mails.get(0).getDirection()).isEqualTo(GrpcDirection.OUT);
 				assertThat(mails.get(0).getSentAt()).isNotNull();
-				assertThat(ZonedDateTime.parse(mails.get(0).getSentAt())).isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS));
+				assertThat(ZonedDateTime.parse(mails.get(0).getSentAt())).isCloseTo(ZonedDateTime.now(), within(61, ChronoUnit.SECONDS));
 				assertThat(mails.get(0).getSentSuccessful()).isTrue();
 			}
 		}
@@ -175,7 +175,7 @@ class PostfachMailITCase {
 				await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
 					assertThat(callGrpcListEndpoint()).hasSize(1).first().satisfies(mail -> {
 						assertThat(mail.getSentAt()).isNotNull();
-						assertThat(ZonedDateTime.parse(mail.getSentAt())).isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS));
+						assertThat(ZonedDateTime.parse(mail.getSentAt())).isCloseTo(ZonedDateTime.now(), within(61, ChronoUnit.SECONDS));
 						assertThat(mail.getSentSuccessful()).isFalse();
 					});
 				});