diff --git a/Jenkinsfile b/Jenkinsfile
index 81a74c97531aa991385a034402a13fbbb38786b6..c8239e400dc3ecbd0d35fe9332489a4d4a04d914 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -44,6 +44,27 @@ pipeline {
                 }
             }
         }
+
+        stage('Set Version') {
+          when {
+            not {
+                anyOf {
+                    branch 'master'
+                    branch 'release'
+                }
+            }
+          }
+          steps {
+                script {
+                    FAILED_STAGE=env.STAGE_NAME
+                    JAR_TAG = getPomVersion('pom.xml').replace("SNAPSHOT", "${env.BRANCH_NAME}-SNAPSHOT")
+                }
+                configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
+                    sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true"
+                    
+                }
+          }
+        } 
         
         stage('Build VorgangManager') {
           steps {
@@ -58,18 +79,13 @@ pipeline {
         }
         
         stage('Deploy to Nexus'){
-            when {
-                anyOf {
-                    branch 'master'
-                    branch 'release'
-                }
-            }
             steps {
                 script {
                     FAILED_STAGE = env.STAGE_NAME
                 }
                 configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
                     sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -DskipTests deploy -Dmaven.wagon.http.retryHandler.count=3'
+                    sh "mvn -s $MAVEN_SETTINGS versions:revert"
                 }
             }
         }
diff --git a/bescheid-manager/pom.xml b/bescheid-manager/pom.xml
index 29c0c07efd75e9fb1f25d046b4ffdf824032bf9a..27acb0c4ffd60cdf9acd3a905de168d91ac7417e 100644
--- a/bescheid-manager/pom.xml
+++ b/bescheid-manager/pom.xml
@@ -5,19 +5,19 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.2.0</version>
+		<version>4.3.0</version>
 		<relativePath />
 	</parent>
 
 	<groupId>de.ozgcloud.bescheid</groupId>
 	<artifactId>bescheid-manager</artifactId>
 	<name>OZG-Cloud Bescheid Manager</name>
-	<version>1.13.0-SNAPSHOT</version>
+	<version>1.15.0-SNAPSHOT</version>
 
 	<properties>
-		<vorgang-manager.version>2.9.0-SNAPSHOT</vorgang-manager.version>
-		<nachrichten-manager.version>2.9.0-SNAPSHOT</nachrichten-manager.version>
-		<api-lib.version>0.10.0-SNAPSHOT</api-lib.version>
+		<vorgang-manager.version>2.9.0</vorgang-manager.version>
+		<nachrichten-manager.version>2.9.0</nachrichten-manager.version>
+		<api-lib.version>0.10.0</api-lib.version>
 	</properties>
 
 	<dependencies>
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 2016ea933f0c61b98a22b8ef0e04492e0df6af6e..49fbce9beef117e017788ae682323792bdb7478e 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java
@@ -30,6 +30,7 @@ import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.event.EventListener;
@@ -48,6 +49,7 @@ import de.ozgcloud.command.CommandCreatedEvent;
 import de.ozgcloud.command.CommandFailedEvent;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.document.Document;
 import de.ozgcloud.document.DocumentService;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -79,10 +81,9 @@ class BescheidEventListener {
 	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())}";
 
-	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";
+	static final String VORGANG_ID_BODY_KEY = "vorgangId";
+	static final String BESCHEID_VOM_BODY_KEY = "bescheidVom";
+	static final String GENEHMIGT_BODY_KEY = "genehmigt";
 
 	private static final String LOG_MESSAGE_TEMPLATE = "{}. Command failed.";
 	private static final String ERROR_MESSAGE_TEMPLATE = "Error on executing %s Command.";
@@ -130,10 +131,16 @@ class BescheidEventListener {
 	}
 
 	void doUpdateBescheid(Command command) {
-		attachedItemService.updateBescheidDraft(command);
+		attachedItemService.updateBescheidDraft(command, getDocument(command));
 		eventPublisher.publishEvent(new BescheidUpdatedEvent(command));
 	}
 
+	Optional<Document> getDocument(Command command) {
+		return Optional.ofNullable(MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_BESCHEID_DOCUMENT))
+				.map(StringUtils::trimToNull)
+				.map(documentService::getDocument);
+	}
+
 	@EventListener(condition = IS_CREATE_BESCHEID_DOCUMENT)
 	public void onCreatedBescheidDocument(CommandCreatedEvent event) {
 		runWithSecurityContext(event.getSource(), this::doCreateBescheidDocument);
@@ -174,10 +181,9 @@ class BescheidEventListener {
 		var builder = BescheidRequest.builder();
 
 		builder.vorgangId(VorgangId.from(command.getVorgangId()));
-		Optional.ofNullable(eventBody.get(BESCHEID_VOM_BODYKEY))
-				.map(String.class::cast).map(LocalDate::parse).ifPresent(builder::bescheidVom);
-		Optional.ofNullable(eventBody.get(GENEHMIGT_BODYKEY))
-				.map(Object::toString).map(Boolean::valueOf)
+		Optional.ofNullable(MapUtils.getString(eventBody, BESCHEID_VOM_BODY_KEY))
+				.map(LocalDate::parse).ifPresent(builder::bescheidVom);
+		Optional.ofNullable(MapUtils.getBoolean(eventBody, GENEHMIGT_BODY_KEY))
 				.ifPresentOrElse(builder::genehmigt, () -> builder.genehmigt(true));
 		builder.createFor(userProfileService.getUserProfile());
 
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidManagerConfiguration.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidManagerConfiguration.java
index 4cf07e25e4cca9e218a57be1ae82d837639c58d9..824f5ad0608057bec16dd5ef1d701fe481d9ff71 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidManagerConfiguration.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidManagerConfiguration.java
@@ -36,10 +36,12 @@ import net.devh.boot.grpc.client.inject.GrpcClient;
 @Configuration
 public class BescheidManagerConfiguration {
 
+	public static final String COMMAND_SERVICE_NAME = "bescheid_OzgCloudCommandService";
+
 	@GrpcClient("command-manager")
 	private CommandServiceGrpc.CommandServiceBlockingStub commandServiceStub;
 
-	@Bean("bescheid_OzgCloudCommandService")
+	@Bean(COMMAND_SERVICE_NAME)
 	OzgCloudCommandService grpcOzgCloudCommandService(CommandMapper commandMapper, BescheidManagerCallContextProvider contextProvider) {
 		return new GrpcOzgCloudCommandService(commandServiceStub, commandMapper, contextProvider,
 				GrpcOzgCloudCommandService.DEFAULT_COMMAND_REQUEST_THRESHOLD_MILLIS);
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 4c25578f1064dd54ad74ddada10e25b612d8a503..180d95031923d482c4f25f3f42f42ba0620b111d 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
@@ -32,11 +32,10 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
-import java.util.function.Predicate;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.apache.commons.collections.MapUtils;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -47,15 +46,18 @@ 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.BescheidManagerConfiguration;
 import de.ozgcloud.bescheid.common.callcontext.CallContextUser;
 import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.bescheid.common.freemarker.TemplateHandler;
 import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.common.errorhandling.TechnicalException;
+import de.ozgcloud.document.Document;
 import lombok.extern.log4j.Log4j2;
 
-@Service
 @Log4j2
+@Service
 public class AttachedItemService {
 
 	public static final String BESCHEID_ITEM_NAME = "Bescheid";
@@ -64,10 +66,11 @@ public class AttachedItemService {
 	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 -> !ArrayUtils.contains(Bescheid.SendBy.values(), sendBy);
+	static final String DEFAULT_SUBJECT = "Ihr Bescheid zum Antrag";
+	static final String DEFAULT_TEMPLATE_FILE = "bescheid.nachrichtTemplate.txt.ftlh";
 
+	@Qualifier(BescheidManagerConfiguration.COMMAND_SERVICE_NAME)
 	@Autowired
-	@Qualifier("bescheid_OzgCloudCommandService")
 	private OzgCloudCommandService commandService;
 	@Autowired
 	private VorgangAttachedItemRemoteService remoteService;
@@ -75,13 +78,15 @@ public class AttachedItemService {
 	private CommandMapper commandMapper;
 	@Autowired
 	private CurrentUserService currentUserService;
+	@Autowired
+	private TemplateHandler templateHandler;
 
 	public Optional<AttachedItem> findBescheidItem(String vorgangId) {
 		return remoteService.findBescheidDraft(vorgangId);
 	}
 
-	public AttachedItem getBescheidItem(String id) {
-		return remoteService.getBescheid(id);
+	public AttachedItem getItem(String id) {
+		return remoteService.getItem(id);
 	}
 
 	public String createBescheidDraft(Command command) {
@@ -90,45 +95,96 @@ public class AttachedItemService {
 				.map(bescheid -> overrideAttachedItem(bescheid, command)).orElseGet(() -> createAttachedItem(command));
 	}
 
+	void validateBescheidData(Map<String, Object> bodyObject) {
+		validateBescheidField(bodyObject, Bescheid.FIELD_BESCHIEDEN_AM);
+		validateBescheidField(bodyObject, Bescheid.FIELD_BEWILLIGT);
+		validateSendBy(getSendBy(bodyObject));
+	}
+
+	void validateBescheidField(Map<String, Object> bescheid, String field) {
+		if (isNull(bescheid.get(field))) {
+			throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(field));
+		}
+	}
+
+	void validateSendBy(String sendBy) {
+		if (isUnknownSendByValue(sendBy)) {
+			var possibleSendByValues = Arrays.stream(Bescheid.SendBy.values()).map(Bescheid.SendBy::name).collect(Collectors.joining(","));
+			var message = String.format("Unexpected value for field '%s': %s. Allowed are: %s", Bescheid.FIELD_SEND_BY, sendBy, possibleSendByValues);
+			throw new TechnicalException(message);
+		}
+	}
+
+	private boolean isUnknownSendByValue(String sendByValue) {
+		return Arrays.stream(Bescheid.SendBy.values()).noneMatch(sendBy -> sendBy.hasValue(sendByValue));
+	}
+
 	String overrideAttachedItem(AttachedItem bescheidItem, Command command) {
-		var bodyObject = buildAttachedItemAsMap(command, buildItemMapWithAllBescheidFields(command));
-		var finishedOzgCloudCommand = commandService.createAndWaitUntilDone(buildUpdateAttachedItemCommand(bescheidItem, bodyObject));
+		var attachedItem = buildAttachedItemAsMap(command, buildItemMapWithAllBescheidFields(command));
+		var finishedOzgCloudCommand = commandService.createAndWaitUntilDone(buildPatchAttachedItemCommand(bescheidItem, attachedItem));
 		return finishedOzgCloudCommand.getCreatedResource();
 	}
 
 	Map<String, Object> buildItemMapWithAllBescheidFields(Command command) {
 		var result = new HashMap<String, Object>();
-		result.put(Bescheid.FIELD_BESCHIEDEN_AM, command.getBodyObject().get(Bescheid.FIELD_BESCHIEDEN_AM));
-		result.put(Bescheid.FIELD_BEWILLIGT, command.getBodyObject().get(Bescheid.FIELD_BEWILLIGT));
-		result.put(Bescheid.FIELD_BESCHEID_DOCUMENT, command.getBodyObject().get(Bescheid.FIELD_BESCHEID_DOCUMENT));
-		result.put(Bescheid.FIELD_SEND_BY, command.getBodyObject().get(Bescheid.FIELD_SEND_BY));
-		result.put(Bescheid.FIELD_NACHRICHT_TEXT, command.getBodyObject().get(Bescheid.FIELD_NACHRICHT_TEXT));
-		result.put(Bescheid.FIELD_NACHRICHT_SUBJECT, command.getBodyObject().get(Bescheid.FIELD_NACHRICHT_SUBJECT));
-		result.put(Bescheid.FIELD_ATTACHMENTS, command.getBodyObject().get(Bescheid.FIELD_ATTACHMENTS));
+		result.put(Bescheid.FIELD_BESCHIEDEN_AM, MapUtils.getObject(command.getBodyObject(), Bescheid.FIELD_BESCHIEDEN_AM));
+		result.put(Bescheid.FIELD_BEWILLIGT, MapUtils.getObject(command.getBodyObject(), Bescheid.FIELD_BEWILLIGT));
+		result.put(Bescheid.FIELD_BESCHEID_DOCUMENT, MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_BESCHEID_DOCUMENT));
+		result.put(Bescheid.FIELD_SEND_BY, getSendBy(command.getBodyObject()));
+		result.put(Bescheid.FIELD_NACHRICHT_TEXT, MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_NACHRICHT_TEXT));
+		result.put(Bescheid.FIELD_NACHRICHT_SUBJECT, MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_NACHRICHT_SUBJECT));
+		result.put(Bescheid.FIELD_ATTACHMENTS, MapUtils.getObject(command.getBodyObject(), Bescheid.FIELD_ATTACHMENTS));
 		return result;
 	}
 
-	public void updateBescheidDraft(Command command) {
-		var bescheidItem = remoteService.getBescheid(command.getRelationId());
-		validateBescheidStatus(bescheidItem);
+	private String getSendBy(Map<String, Object> body) {
+		return MapUtils.getString(body, Bescheid.FIELD_SEND_BY);
+	}
+
+	public void updateBescheidDraft(Command command, Optional<Document> document) {
+		createCommandAfterValidation(command, bescheid -> buildUpdateBescheidDraftCommand(command, document, bescheid));
+	}
+
+	private OzgCloudCommand buildUpdateBescheidDraftCommand(Command command, Optional<Document> document, AttachedItem bescheidItem) {
+		return buildPatchAttachedItemCommand(bescheidItem, buildAttachedItemAsMap(command, buildBescheidItemAsMap(command, document)));
+	}
+
+	Map<String, Object> buildBescheidItemAsMap(Command command, Optional<Document> newDocument) {
 		var itemMap = buildItemMapWithExistingBescheidFields(command);
-		var bodyObject = buildAttachedItemAsMap(command, itemMap);
-		commandService.createAndWaitUntilDone(buildUpdateAttachedItemCommand(bescheidItem, bodyObject));
+		var savedBescheid = getItem(command.getRelationId());
+
+		getNewNachrichtText(savedBescheid, newDocument).ifPresent(nachrichtText -> itemMap.put(Bescheid.FIELD_NACHRICHT_TEXT, nachrichtText));
+
+		return itemMap;
+	}
+
+	Optional<String> getNewNachrichtText(AttachedItem bescheidItem, Optional<Document> newDocument) {
+		return newDocument
+				.filter(document -> isDifferentDocument(document, bescheidItem))
+				.map(Document::getNachrichtText)
+				.filter(StringUtils::isNotBlank);
 	}
 
-	OzgCloudCommand buildUpdateAttachedItemCommand(AttachedItem bescheidItem, Map<String, Object> bodyObject) {
+	boolean isDifferentDocument(Document document, AttachedItem bescheidItem) {
+		return !StringUtils.equals(document.getId(), getBescheidDocumentId(bescheidItem));
+	}
+
+	private String getBescheidDocumentId(AttachedItem item) {
+		return MapUtils.getString(item.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT);
+	}
+
+	OzgCloudCommand buildPatchAttachedItemCommand(AttachedItem bescheidItem, Map<String, Object> bodyObject) {
 		return OzgCloudCommand.builder()
 				.vorgangId(commandMapper.toOzgCloudVorgangId(bescheidItem.getVorgangId()))
 				.relationId(commandMapper.mapRelationId(bescheidItem.getId()))
 				.relationVersion(bescheidItem.getVersion())
-				.order(UPDATE_ATTACHED_ITEM_ORDER)
+				.order(PATCH_ATTACHED_ITEM)
 				.bodyObject(bodyObject)
 				.build();
 	}
 
 	String createAttachedItem(Command command) {
-		var finishedOzgCloudCommand = commandService.createAndWaitUntilDone(buildCreateAttachedItemCommand(command));
-		return finishedOzgCloudCommand.getCreatedResource();
+		return commandService.createAndWaitUntilDone(buildCreateAttachedItemCommand(command)).getCreatedResource();
 	}
 
 	OzgCloudCommand buildCreateAttachedItemCommand(Command command) {
@@ -136,21 +192,31 @@ public class AttachedItemService {
 				.vorgangId(commandMapper.toOzgCloudVorgangId(command.getVorgangId()))
 				.relationId(commandMapper.mapRelationId(command.getRelationId()))
 				.order(CREATE_ATTACHED_ITEM_ORDER)
-				.bodyObject(buildAttachedItemAsMap(command, buildItemMapWithExistingBescheidFields(command))).build();
+				.bodyObject(buildAttachedItemAsMap(command, buildCreateBescheidItemMap(command))).build();
 	}
 
-	Map<String, Object> buildAttachedItemAsMap(Command command, Map<String, Object> itemmap) {
+	Map<String, Object> buildAttachedItemAsMap(Command command, Map<String, Object> itemMap) {
 		var result = new HashMap<String, Object>();
 		result.put(AttachedItem.PROPERTY_VORGANG_ID, command.getVorgangId());
 		result.put(AttachedItem.PROPERTY_CLIENT, BescheidCallContextAttachingInterceptor.BESCHEID_MANAGER_CLIENT_NAME);
 		result.put(AttachedItem.PROPERTY_ITEM_NAME, BESCHEID_ITEM_NAME);
-		result.put(AttachedItem.PROPERTY_ITEM, itemmap);
+		result.put(AttachedItem.PROPERTY_ITEM, itemMap);
+		return result;
+	}
+
+	Map<String, Object> buildCreateBescheidItemMap(Command command) {
+		var result = buildItemMapWithExistingBescheidFields(command);
+		if (StringUtils.isEmpty(getNachrichtSubject(command))) {
+			result.put(Bescheid.FIELD_NACHRICHT_SUBJECT, DEFAULT_SUBJECT);
+		}
+		if (StringUtils.isEmpty(getNachrichtText(command))) {
+			result.put(Bescheid.FIELD_NACHRICHT_TEXT, templateHandler.getRawTemplate(DEFAULT_TEMPLATE_FILE));
+		}
 		return result;
 	}
 
 	Map<String, Object> buildItemMapWithExistingBescheidFields(Command command) {
-		var result = new HashMap<String, Object>();
-		result.put(Bescheid.FIELD_STATUS, Bescheid.Status.DRAFT.name());
+		var result = buildItemMap(Bescheid.Status.DRAFT);
 		addValueFromMapIfExists(command.getBodyObject(), Bescheid.FIELD_BESCHIEDEN_AM, result);
 		addValueFromMapIfExists(command.getBodyObject(), Bescheid.FIELD_BEWILLIGT, result);
 		addValueFromMapIfExists(command.getBodyObject(), Bescheid.FIELD_BESCHEID_DOCUMENT, result);
@@ -161,30 +227,28 @@ public class AttachedItemService {
 		return result;
 	}
 
+	private String getNachrichtSubject(Command command) {
+		return MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_NACHRICHT_SUBJECT);
+	}
+
+	private String getNachrichtText(Command command) {
+		return MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_NACHRICHT_TEXT);
+	}
+
 	void addValueFromMapIfExists(Map<String, Object> sourceMap, String key, Map<String, Object> targetMap) {
 		if (sourceMap.containsKey(key)) {
 			targetMap.put(key, sourceMap.get(key));
 		}
 	}
 
-	void validateBescheidData(Map<String, Object> bodyObject) {
-		if (isNull(bodyObject.get(Bescheid.FIELD_BESCHIEDEN_AM))) {
-			throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(Bescheid.FIELD_BESCHIEDEN_AM));
-		}
-		if (isNull(bodyObject.get(Bescheid.FIELD_BEWILLIGT))) {
-			throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(Bescheid.FIELD_BEWILLIGT));
-		}
-		Optional.ofNullable(MapUtils.getString(bodyObject, Bescheid.FIELD_SEND_BY)).filter(notExpectedSendByValue)
-				.ifPresent(sendBy ->
-						LOG.warn("Unexpected value for field '%s': %s. Allowed are: %s".formatted(Bescheid.FIELD_SEND_BY, sendBy,
-								Arrays.stream(Bescheid.SendBy.values()).map(Bescheid.SendBy::name).collect(Collectors.joining(","))))
-				);
+	public void deleteBescheidDraft(Command command) {
+		createCommandAfterValidation(command, bescheid -> buildDeleteItemCommand(command, bescheid));
 	}
 
-	public void deleteBescheidDraft(Command command) {
-		var bescheid = remoteService.getBescheid(command.getRelationId());
+	private void createCommandAfterValidation(Command command, Function<AttachedItem, OzgCloudCommand> buildCommand) {
+		var bescheid = remoteService.getItem(command.getRelationId());
 		validateBescheidStatus(bescheid);
-		commandService.createAndWaitUntilDone(buildDeleteItemCommand(command, bescheid));
+		commandService.createAndWaitUntilDone(buildCommand.apply(bescheid));
 	}
 
 	void validateBescheidStatus(AttachedItem bescheid) {
@@ -203,13 +267,8 @@ public class AttachedItemService {
 				.build();
 	}
 
-	public AttachedItem getItem(String id) {
-		return remoteService.getItem(id);
-	}
-
 	public void setBescheidSentStatus(String id, long version) {
-		var bodyObject = buildBescheidSentStatusBodyObject(id);
-		commandService.createAndWaitUntilDone(buildPatchBescheidCommand(id, version, bodyObject));
+		createPatchCommand(id, version, buildBescheidSentStatusBodyObject(id));
 	}
 
 	Map<String, Object> buildBescheidSentStatusBodyObject(String bescheidId) {
@@ -226,8 +285,7 @@ public class AttachedItemService {
 	Map<String, Object> buildSentInfoMap() {
 		return Map.of(
 				Bescheid.FIELD_SENT_AT, ZonedDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME),
-				Bescheid.FIELD_SENT_BY, getUserId()
-		);
+				Bescheid.FIELD_SENT_BY, getUserId());
 	}
 
 	String getUserId() {
@@ -235,8 +293,11 @@ public class AttachedItemService {
 	}
 
 	public void revertBescheidSendStatus(String id, long version) {
-		var bodyObject = buildRevertBescheidSentStatusBodyObject(id);
-		commandService.createAndWaitUntilDone(buildPatchBescheidCommand(id, version, bodyObject));
+		createPatchCommand(id, version, buildRevertBescheidSentStatusBodyObject(id));
+	}
+
+	private void createPatchCommand(String id, long version, Map<String, Object> itemMap) {
+		commandService.createAndWaitUntilDone(buildPatchBescheidCommand(id, version, itemMap));
 	}
 
 	Map<String, Object> buildRevertBescheidSentStatusBodyObject(String bescheidId) {
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 ff07e6d16434e02343d1a07605c8351330d640ba..418361772a1446dfe7923822906111ae4b5c46a3 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
@@ -97,11 +97,6 @@ class VorgangAttachedItemRemoteService {
 		return Bescheid.Status.DRAFT.hasValue(bescheidItem.getItem().get(Bescheid.FIELD_STATUS));
 	}
 
-	public AttachedItem getBescheid(String bescheidId) {
-		var grpcVorgangAttachedItemResponse = getServiceStub().getById(buildGetByIdRequest(bescheidId));
-		return attachedItemMapper.mapFromVorgangAttachedItem(grpcVorgangAttachedItemResponse.getVorgangAttachedItem());
-	}
-
 	public AttachedItem getItem(String id) {
 		var grpcVorgangAttachedItemResponse = getServiceStub().getById(buildGetByIdRequest(id));
 		return attachedItemMapper.mapFromVorgangAttachedItem(grpcVorgangAttachedItemResponse.getVorgangAttachedItem());
diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandler.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..8355a9ffca2cf8b9ad91449133240e11745131c5
--- /dev/null
+++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandler.java
@@ -0,0 +1,43 @@
+package de.ozgcloud.bescheid.common.freemarker;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor
+public class TemplateHandler {
+
+	private final Configuration freemarkerConfig;
+
+	public String getRawTemplate(String templateName) {
+		return getTemplate(templateName).toString();
+	}
+
+	public String fillTemplate(String templateName, Object dataModel) {
+		try {
+			var template = getTemplate(templateName);
+			var stringWriter = new StringWriter();
+			template.process(dataModel, stringWriter);
+			return stringWriter.toString();
+
+		} catch (IOException | TemplateException e) {
+			throw new TechnicalException("Error filling template", e);
+		}
+	}
+
+	private Template getTemplate(String templateName) {
+		try {
+			return freemarkerConfig.getTemplate(templateName);
+		} catch (IOException e) {
+			throw new TechnicalException("Error loading mail template", e);
+		}
+	}
+}
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 0f20aad8d7458fbe5a324168462b52cafc224e94..ce261151ddaa6d682521e59cfbb806c21c033656 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
@@ -1,7 +1,5 @@
 package de.ozgcloud.bescheid.nachricht;
 
-import java.io.IOException;
-import java.io.StringWriter;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -17,13 +15,11 @@ 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.common.freemarker.TemplateHandler;
 import de.ozgcloud.bescheid.vorgang.Vorgang;
 import de.ozgcloud.bescheid.vorgang.Vorgang.PostfachAddress;
 import de.ozgcloud.common.binaryfile.FileId;
 import de.ozgcloud.common.errorhandling.TechnicalException;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.TemplateException;
 import lombok.extern.log4j.Log4j2;
 
 @Service
@@ -41,14 +37,14 @@ public class NachrichtService {
 	@Autowired
 	private NachrichtRemoteService remoteService;
 	@Autowired
-	private Configuration freemarkerCfg;
-	@Autowired
 	@Qualifier("bescheid_OzgCloudCommandService")
 	private OzgCloudCommandService commandService;
 	@Autowired
 	private CommandMapper commandMapper;
+	@Autowired
+	private TemplateHandler templateHandler;
 
-	static final String SUBJECT = "Ihr Antrag";
+	static final String SUBJECT = "Ihr Bescheid zum Antrag";
 
 	private static final String TEMPLATE_FILE = "bescheid.nachrichtTemplate.txt.ftlh";
 
@@ -69,27 +65,7 @@ public class NachrichtService {
 
 	String buildMessage(Bescheid bescheid) {
 		return bescheid.getNachrichtText()
-				.orElseGet(() -> fillTemplate(TEMPLATE_FILE, bescheid));
-	}
-
-	String fillTemplate(String templateName, Object dataModel) {
-		try {
-			Template template = getTemplate(templateName);
-			StringWriter stringWriter = new StringWriter();
-			template.process(dataModel, stringWriter);
-			return stringWriter.toString();
-
-		} catch (IOException | TemplateException e) {
-			throw new TechnicalException("Error filling template", e);
-		}
-	}
-
-	Template getTemplate(String templateName) {
-		try {
-			return freemarkerCfg.getTemplate(templateName);
-		} catch (IOException e) {
-			throw new TechnicalException("Error loading mail template", e);
-		}
+				.orElseGet(() -> templateHandler.fillTemplate(TEMPLATE_FILE, bescheid));
 	}
 
 	public void sendNachricht(Bescheid bescheid) {
@@ -112,8 +88,7 @@ public class NachrichtService {
 				FIELD_SUBJECT, bescheid.getNachrichtSubject().orElse(SUBJECT),
 				FIELD_MAIL_BODY, bescheid.getNachrichtText().orElse(StringUtils.EMPTY),
 				FIELD_ATTACHMENTS, buildAttachments(bescheid),
-				Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, buildPostfachAddress(bescheid)
-		);
+				Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, buildPostfachAddress(bescheid));
 
 	}
 
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 54d398b8c5d55ce78f57da999f09df3326d23d99..a49c8581074b511f05bc5c13b0666933753a457e 100644
--- a/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java
+++ b/bescheid-manager/src/main/java/de/ozgcloud/document/DocumentService.java
@@ -82,7 +82,7 @@ public class DocumentService {
 	}
 
 	void validateBescheidItem(String bescheidId) {
-		var bescheidData = attachedItemService.getBescheidItem(bescheidId).getItem();
+		var bescheidData = attachedItemService.getItem(bescheidId).getItem();
 		var status = MapUtils.getString(bescheidData, Bescheid.FIELD_STATUS);
 		if (Bescheid.Status.DRAFT.not(status)) {
 			throw new TechnicalException("Bescheid is not in draft status");
diff --git a/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh b/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh
index 861bcc67d7f7cffdd9fc2608a6378b8baf23454b..368804ed6ea51f622c32a2e90a21d5f1dd00763f 100644
--- a/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh
+++ b/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh
@@ -1,8 +1,7 @@
-Sehr geehrte/r Antragstellende/r,
+Sehr geehrte/r Antragsteller/in,
 
-ihr Antrag wurde <#if genehmigt>genehmigt<#else>abgelehnt</#if>.
+im Folgenden erhalten Sie Ihren Bescheid.
 
-Sie können innerhalb von vier Wochen Einspruch einlegen.
+Mit freundlichen Grüßen
 
-Mit freundlichen Grüßen,
 Ihre Verwaltung
\ No newline at end of file
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
index 4f6cf30d7290c65804d6a0e36ac0615b0c338a94..96c5ef5dafcba70a2f79b18ae9d7aa8b24bf8670 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
@@ -105,7 +105,7 @@ class BescheidEventListenerITCase {
 		void shouldCallService() {
 			publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(command));
 
-			verify(attachedItemService).updateBescheidDraft(any());
+			verify(attachedItemService).updateBescheidDraft(any(), any());
 		}
 
 		@Test
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 1288fbea070e6808012351618bf72a299175d5c1..823d76cd45304b38173621fa521a6e3cbec6820e 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java
@@ -7,6 +7,7 @@ import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Consumer;
 
 import org.junit.jupiter.api.Assertions;
@@ -27,6 +28,7 @@ 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.freemarker.TemplateHandler;
 import de.ozgcloud.bescheid.common.user.UserProfile;
 import de.ozgcloud.bescheid.common.user.UserProfileService;
 import de.ozgcloud.bescheid.common.user.UserProfileTestFactory;
@@ -37,7 +39,9 @@ import de.ozgcloud.command.CommandFailedEvent;
 import de.ozgcloud.command.CommandTestFactory;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.document.BescheidDocumentCreatedEvent;
+import de.ozgcloud.document.Document;
 import de.ozgcloud.document.DocumentService;
+import de.ozgcloud.document.DocumentTestFactory;
 
 class BescheidEventListenerTest {
 
@@ -64,6 +68,8 @@ class BescheidEventListenerTest {
 	private CurrentUserService userService;
 	@Mock
 	private UserProfileService userProfileService;
+	@Mock
+	private TemplateHandler templateHandler;
 
 	@Nested
 	class TestOnCreateBescheidCommand {
@@ -90,9 +96,9 @@ class BescheidEventListenerTest {
 
 		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))
+						Map.of(VORGANG_ID_BODY_KEY, VORGANG_ID.toString(),
+								BESCHEID_VOM_BODY_KEY, BESCHEID_VOM_STRING,
+								GENEHMIGT_BODY_KEY, GENEHMIGT))
 				.build();
 
 		@Captor
@@ -177,8 +183,8 @@ class BescheidEventListenerTest {
 		private static final Command COMMAND = CommandTestFactory.createBuilder()
 				.vorgangId(VORGANG_ID.toString())
 				.bodyObject(
-						Map.of(BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING,
-								GENEHMIGT_BODYKEY, false))
+						Map.of(BESCHEID_VOM_BODY_KEY, BESCHEID_VOM_STRING,
+								GENEHMIGT_BODY_KEY, false))
 				.build();
 
 		@Test
@@ -207,7 +213,7 @@ class BescheidEventListenerTest {
 			Command command = CommandTestFactory.createBuilder()
 					.vorgangId(VORGANG_ID.toString())
 					.bodyObject(
-							Map.of(BESCHEID_VOM_BODYKEY, BESCHEID_VOM_STRING))
+							Map.of(BESCHEID_VOM_BODY_KEY, BESCHEID_VOM_STRING))
 					.build();
 
 			var request = listener.createRequest(command);
@@ -303,12 +309,25 @@ class BescheidEventListenerTest {
 		private ArgumentCaptor<BescheidUpdatedEvent> bescheidUpdatedEventCaptor;
 
 		private final Command command = CommandTestFactory.createBuilder().build();
+		private final Document document = DocumentTestFactory.create();
+
+		@BeforeEach
+		void mock() {
+			doReturn(Optional.of(document)).when(listener).getDocument(command);
+		}
+
+		@Test
+		void shouldGetDocument() {
+			listener.doUpdateBescheid(command);
+
+			verify(listener).getDocument(command);
+		}
 
 		@Test
 		void shouldCallUpdateBescheidDraft() {
 			listener.doUpdateBescheid(command);
 
-			verify(attachedItemService).updateBescheidDraft(command);
+			verify(attachedItemService).updateBescheidDraft(command, Optional.of(document));
 		}
 
 		@Test
@@ -318,7 +337,50 @@ class BescheidEventListenerTest {
 			verify(eventPublisher).publishEvent(bescheidUpdatedEventCaptor.capture());
 			assertThat(bescheidUpdatedEventCaptor.getValue().getCommand()).isSameAs(command);
 		}
+	}
+
+	@DisplayName("Get document")
+	@Nested
+	class TestGetDocument {
+
+		@DisplayName("if exists")
+		@Nested
+		class TestIfExists {
+
+			private final Command command = CommandTestFactory.createBuilder().bodyObject(AttachedItemTestFactory.createBescheidItem()).build();
+
+			@BeforeEach
+			void mock() {
+				when(documentService.getDocument(any())).thenReturn(DocumentTestFactory.create());
+			}
+
+			@Test
+			void shouldCallService() {
+				listener.getDocument(command);
+
+				verify(documentService).getDocument(AttachedItemTestFactory.BESCHEID_DOCUMENT);
+			}
+
+			@Test
+			void shouldReturnValue() {
+				var document = listener.getDocument(command);
+
+				assertThat(document.get()).usingRecursiveComparison().isEqualTo(DocumentTestFactory.create());
+			}
+		}
+
+		@Test
+		void shouldReturnEmptyIfNoDocumentExists() {
+			var document = listener.getDocument(createCommandWithoutDocument());
+
+			assertThat(document).isEmpty();
+		}
 
+		private Command createCommandWithoutDocument() {
+			var bescheidItemMap = AttachedItemTestFactory.createBescheidItem();
+			bescheidItemMap.remove(AttachedItemTestFactory.BESCHEID_DOCUMENT);
+			return CommandTestFactory.createBuilder().bodyObject(bescheidItemMap).build();
+		}
 	}
 
 	@Nested
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 ce3d09c04038035e8f966a162f7d6ca99bdd7fac..726edcf3e90a969633d5f3caf885298c65334776 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
@@ -25,6 +25,7 @@ package de.ozgcloud.bescheid.attacheditem;
 
 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.time.ZonedDateTime;
@@ -36,30 +37,40 @@ import java.util.Map;
 import java.util.Optional;
 
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 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.junit.jupiter.params.provider.ValueSource;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
 
+import com.thedeanda.lorem.LoremIpsum;
+
 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;
 import de.ozgcloud.bescheid.Bescheid;
+import de.ozgcloud.bescheid.Bescheid.SendBy;
 import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor;
 import de.ozgcloud.bescheid.common.callcontext.CallContextUser;
 import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
+import de.ozgcloud.bescheid.common.freemarker.TemplateHandler;
 import de.ozgcloud.bescheid.vorgang.VorgangId;
 import de.ozgcloud.bescheid.vorgang.VorgangTestFactory;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandTestFactory;
 import de.ozgcloud.common.errorhandling.TechnicalException;
+import de.ozgcloud.document.Document;
+import de.ozgcloud.document.DocumentTestFactory;
 import io.grpc.StatusRuntimeException;
 
 class AttachedItemServiceTest {
@@ -76,6 +87,8 @@ class AttachedItemServiceTest {
 	private VorgangAttachedItemRemoteService remoteService;
 	@Mock
 	private CurrentUserService currentUserService;
+	@Mock
+	private TemplateHandler templateHandler;
 
 	@Nested
 	class TestFindBescheidItem {
@@ -98,27 +111,6 @@ class AttachedItemServiceTest {
 		}
 	}
 
-	@Nested
-	class TestGetBescheidItem {
-
-		@Test
-		void shouldCallGetBescheid() {
-			service.getBescheidItem(AttachedItemTestFactory.ID);
-
-			verify(remoteService).getBescheid(AttachedItemTestFactory.ID);
-		}
-
-		@Test
-		void shouldReturnBescheidItem() {
-			var expected = AttachedItemTestFactory.createBescheid();
-			when(remoteService.getBescheid(anyString())).thenReturn(expected);
-
-			var actual = service.getBescheidItem(AttachedItemTestFactory.ID);
-
-			assertThat(actual).isEqualTo(expected);
-		}
-	}
-
 	@Nested
 	class TestCreateBescheidDraft {
 
@@ -247,7 +239,7 @@ class AttachedItemServiceTest {
 			@Test
 			void shouldCallBuildAttachedItemAsMap() {
 				var expectedItemMap = Map.<String, Object>of("key", "value");
-				doReturn(expectedItemMap).when(service).buildItemMapWithExistingBescheidFields(any());
+				doReturn(expectedItemMap).when(service).buildCreateBescheidItemMap(any());
 
 				service.buildCreateAttachedItemCommand(command);
 
@@ -272,6 +264,87 @@ class AttachedItemServiceTest {
 			}
 		}
 
+		@DisplayName("Build create bescheid itemMap")
+		@Nested
+		class TestBuildCreateBescheidItemMap {
+
+			private static final String TEMPLATE_CONTENT_AS_STRING = LoremIpsum.getInstance().getParagraphs(5, 20);
+
+			@DisplayName("on empty nachricht")
+			@Nested
+			class TestOnEmptyNachricht {
+
+				@Test
+				void shouldHaveDefaultNachrichtSubjectInBodyIfMissing() {
+					var bodyObject = service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					assertThat(bodyObject).containsEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemService.DEFAULT_SUBJECT);
+				}
+
+				@Test
+				void shouldCallTemplateHandler() {
+					when(templateHandler.getRawTemplate(any())).thenReturn(TEMPLATE_CONTENT_AS_STRING);
+
+					service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					verify(templateHandler).getRawTemplate(AttachedItemService.DEFAULT_TEMPLATE_FILE);
+				}
+
+				@Test
+				void shouldHaveDefaultNachrichtTextInBodyIfMissing() {
+					when(templateHandler.getRawTemplate(any())).thenReturn(TEMPLATE_CONTENT_AS_STRING);
+
+					var bodyObject = service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					assertThat(bodyObject).containsEntry(Bescheid.FIELD_NACHRICHT_TEXT, TEMPLATE_CONTENT_AS_STRING);
+				}
+
+				private Command createCommandWithoutNachricht() {
+					var bescheidItemMap = AttachedItemTestFactory.createBescheidItem();
+					bescheidItemMap.put(Bescheid.FIELD_NACHRICHT_SUBJECT, StringUtils.EMPTY);
+					bescheidItemMap.put(Bescheid.FIELD_NACHRICHT_TEXT, StringUtils.EMPTY);
+					return CommandTestFactory.createBuilder().bodyObject(bescheidItemMap).build();
+				}
+			}
+
+			@DisplayName("on non existing nachricht")
+			@Nested
+			class TestNonExistingNachricht {
+
+				@Test
+				void shouldHaveDefaultNachrichtSubjectInBodyIfMissing() {
+					var bodyObject = service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					assertThat(bodyObject).containsEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemService.DEFAULT_SUBJECT);
+				}
+
+				@Test
+				void shouldCallTemplateHandler() {
+					when(templateHandler.getRawTemplate(any())).thenReturn(TEMPLATE_CONTENT_AS_STRING);
+
+					service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					verify(templateHandler).getRawTemplate(AttachedItemService.DEFAULT_TEMPLATE_FILE);
+				}
+
+				@Test
+				void shouldHaveDefaultNachrichtTextInBodyIfMissing() {
+					when(templateHandler.getRawTemplate(any())).thenReturn(TEMPLATE_CONTENT_AS_STRING);
+
+					var bodyObject = service.buildCreateBescheidItemMap(createCommandWithoutNachricht());
+
+					assertThat(bodyObject).containsEntry(Bescheid.FIELD_NACHRICHT_TEXT, TEMPLATE_CONTENT_AS_STRING);
+				}
+
+				private Command createCommandWithoutNachricht() {
+					var bescheidItemMap = AttachedItemTestFactory.createBescheidItem();
+					bescheidItemMap.remove(Bescheid.FIELD_NACHRICHT_SUBJECT);
+					bescheidItemMap.remove(Bescheid.FIELD_NACHRICHT_TEXT);
+					return CommandTestFactory.createBuilder().bodyObject(bescheidItemMap).build();
+				}
+			}
+		}
+
 		@Nested
 		class TestValidateBescheidData {
 
@@ -283,7 +356,7 @@ class AttachedItemServiceTest {
 			@DisplayName(value = "should fail if bescheidAm is missing")
 			@Test
 			void shouldThrowExceptionByBescheidAm() {
-				Map<String, Object> map = Map.of(Bescheid.FIELD_BEWILLIGT, true);
+				var map = Map.<String, Object>of(Bescheid.FIELD_BEWILLIGT, true);
 
 				assertThrows(TechnicalException.class, () -> service.validateBescheidData(map));
 			}
@@ -291,10 +364,37 @@ class AttachedItemServiceTest {
 			@DisplayName(value = "should fail if bewilligt is missing")
 			@Test
 			void shouldThrowExceptionByBewilligt() {
-				Map<String, Object> map = Map.of(Bescheid.FIELD_BESCHIEDEN_AM, "2021-01-01");
+				var map = Map.<String, Object>of(Bescheid.FIELD_BESCHIEDEN_AM, "2021-01-01");
 
 				assertThrows(TechnicalException.class, () -> service.validateBescheidData(map));
 			}
+
+			@Test
+			void shouldValidateSendBy() {
+				var bodyObject = Map.<String, Object>of(Bescheid.FIELD_SEND_BY, SendBy.MANUAL.name());
+				doNothing().when(service).validateBescheidField(any(), any());
+
+				service.validateBescheidData(bodyObject);
+
+				verify(service).validateSendBy(SendBy.MANUAL.name());
+			}
+
+			@DisplayName("validate sendBy")
+			@Nested
+			class TestValidateSendBy {
+
+				@ValueSource(strings = { "not-exist", "" })
+				@ParameterizedTest
+				void shouldThrowExceptionOnNonExistingSendByValue(String sendBy) {
+					assertThrows(TechnicalException.class, () -> service.validateSendBy(sendBy));
+				}
+
+				@EnumSource(names = { "NACHRICHT", "MANUAL" })
+				@ParameterizedTest
+				void shouldNotThrowAnyExceptionOnExistingSendByValue(SendBy sendBy) {
+					assertDoesNotThrow(() -> service.validateSendBy(sendBy.name()));
+				}
+			}
 		}
 	}
 
@@ -306,39 +406,40 @@ class AttachedItemServiceTest {
 
 		private final Command command = CommandTestFactory.createBuilder().relationId(AttachedItemTestFactory.ID).build();
 		private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid();
+		private final Optional<Document> document = Optional.of(DocumentTestFactory.create());
 
 		@BeforeEach
 		void init() {
-			when(remoteService.getBescheid(any())).thenReturn(bescheidItem);
+			when(remoteService.getItem(any())).thenReturn(bescheidItem);
 		}
 
 		@Test
 		void shouldCallGetBescheid() {
-			service.updateBescheidDraft(command);
+			doUpdateBescheidDraft();
 
-			verify(remoteService).getBescheid(AttachedItemTestFactory.ID);
+			verify(remoteService, times(2)).getItem(AttachedItemTestFactory.ID);
 		}
 
 		@Test
 		void shouldCallValidateBescheidStatus() {
-			service.updateBescheidDraft(command);
+			doUpdateBescheidDraft();
 
 			verify(service).validateBescheidStatus(bescheidItem);
 		}
 
 		@Test
-		void shouldCallBuildItemMapWithExistingBescheidFields() {
-			service.updateBescheidDraft(command);
+		void shouldCallBuildUpdateBescheidItemMap() {
+			doUpdateBescheidDraft();
 
-			verify(service).buildItemMapWithExistingBescheidFields(command);
+			verify(service).buildBescheidItemAsMap(command, document);
 		}
 
 		@Test
 		void shouldCallBuildAttachedItemAsMap() {
 			var itemMap = Map.<String, Object>of("key", "value");
-			doReturn(itemMap).when(service).buildItemMapWithExistingBescheidFields(any());
+			doReturn(itemMap).when(service).buildBescheidItemAsMap(any(), any());
 
-			service.updateBescheidDraft(command);
+			doUpdateBescheidDraft();
 
 			verify(service).buildAttachedItemAsMap(command, itemMap);
 		}
@@ -348,27 +449,151 @@ class AttachedItemServiceTest {
 			var expectedBodyObject = Map.<String, Object>of("key", "value");
 			doReturn(expectedBodyObject).when(service).buildAttachedItemAsMap(any(), any());
 
-			service.updateBescheidDraft(command);
+			doUpdateBescheidDraft();
 
-			verify(service).buildUpdateAttachedItemCommand(bescheidItem, expectedBodyObject);
+			verify(service).buildPatchAttachedItemCommand(bescheidItem, expectedBodyObject);
 		}
 
 		@Test
 		void shouldCallCommandService() {
-			doReturn(updateAttachedItemCommand).when(service).buildUpdateAttachedItemCommand(any(), any());
+			doReturn(updateAttachedItemCommand).when(service).buildPatchAttachedItemCommand(any(), any());
 
-			service.updateBescheidDraft(command);
+			doUpdateBescheidDraft();
 
 			verify(commandService).createAndWaitUntilDone(updateAttachedItemCommand);
 		}
 
 		@Test
 		void shouldThrowExceptionWhenNotFound() {
-			when(remoteService.getBescheid(any())).thenThrow(StatusRuntimeException.class);
+			when(remoteService.getItem(any())).thenThrow(StatusRuntimeException.class);
 
-			assertThrows(StatusRuntimeException.class, () -> service.updateBescheidDraft(command));
+			assertThrows(StatusRuntimeException.class, this::doUpdateBescheidDraft);
 		}
 
+		private void doUpdateBescheidDraft() {
+			service.updateBescheidDraft(command, document);
+		}
+	}
+
+	@DisplayName("Build bescheid item")
+	@Nested
+	class TestBuildBescheidItemAsMap {
+
+		private final Optional<Document> document = Optional.of(DocumentTestFactory.create());
+		private final Command command = CommandTestFactory.createBuilder().bodyObject(AttachedItemTestFactory.createBescheidItem()).build();
+		private final AttachedItem bescheidAttachedItem = AttachedItemTestFactory.createBescheid();
+
+		@BeforeEach
+		void mock() {
+			doReturn(bescheidAttachedItem).when(service).getItem(any());
+		}
+
+		@Test
+		void shouldBuildItemMapWithExistingBescheidFields() {
+			service.buildBescheidItemAsMap(command, document);
+
+			verify(service).buildItemMapWithExistingBescheidFields(command);
+		}
+
+		@Test
+		void shouldGetItem() {
+			service.buildBescheidItemAsMap(command, document);
+
+			verify(service).getItem(CommandTestFactory.RELATION_ID);
+		}
+
+		@Test
+		void shouldCallGetNewNachrichtText() {
+			service.buildBescheidItemAsMap(command, document);
+
+			verify(service).getNewNachrichtText(bescheidAttachedItem, document);
+		}
+
+		@Test
+		void shouldOverrideIfNewNachrichtText() {
+			doReturn(Optional.of(DocumentTestFactory.NACHRICHT_TEXT)).when(service).getNewNachrichtText(any(), any());
+
+			var bescheidItem = service.buildBescheidItemAsMap(command, document);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_NACHRICHT_TEXT, DocumentTestFactory.NACHRICHT_TEXT);
+		}
+
+		@Test
+		void shouldKeepNachrichtTextIfNotNew() {
+			doReturn(Optional.empty()).when(service).getNewNachrichtText(any(), any());
+
+			var bescheidItem = service.buildBescheidItemAsMap(command, document);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT);
+		}
+	}
+
+	@DisplayName("Get new nachrichtText")
+	@Nested
+	class TestGetNewNachrichtText {
+
+		private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid();
+
+		@DisplayName("with filled new document")
+		@Nested
+		class TestFilledDocument {
+
+			private final Document document = DocumentTestFactory.create();
+			private final Optional<Document> documentOpt = Optional.of(document);
+
+			@Test
+			void shouldCheckIfIsDifferentDocument() {
+				service.getNewNachrichtText(bescheidItem, documentOpt);
+
+				verify(service).isDifferentDocument(document, bescheidItem);
+			}
+
+			@DisplayName("and different documentId")
+			@Nested
+			class TestAndDifferentDocumentId {
+
+				@BeforeEach
+				void mock() {
+					doReturn(true).when(service).isDifferentDocument(any(), any());
+				}
+
+				@Test
+				void shouldReturnNachrichtTextFromDocument() {
+					var nachrichtText = service.getNewNachrichtText(bescheidItem, documentOpt);
+
+					assertThat(nachrichtText).hasValue(DocumentTestFactory.NACHRICHT_TEXT);
+				}
+			}
+
+			@DisplayName("and same documentId")
+			@Nested
+			class TestAndSameDocumentId {
+
+				@BeforeEach
+				void mock() {
+					doReturn(false).when(service).isDifferentDocument(any(), any());
+				}
+
+				@Test
+				void shouldReturnNachrichtTextFromDocument() {
+					var nachrichtText = service.getNewNachrichtText(bescheidItem, documentOpt);
+
+					assertThat(nachrichtText).isEmpty();
+				}
+			}
+		}
+
+		@DisplayName("with empty document")
+		@Nested
+		class TestWithEmptyDocument {
+
+			@Test
+			void shouldReturnEmpty() {
+				var nachrichtText = service.getNewNachrichtText(bescheidItem, Optional.empty());
+
+				assertThat(nachrichtText).isEmpty();
+			}
+		}
 	}
 
 	@Nested
@@ -447,12 +672,12 @@ class AttachedItemServiceTest {
 
 			service.overrideAttachedItem(bescheidItem, command);
 
-			verify(service).buildUpdateAttachedItemCommand(bescheidItem, bodyObject);
+			verify(service).buildPatchAttachedItemCommand(bescheidItem, bodyObject);
 		}
 
 		@Test
 		void shouldCallCommandService() {
-			doReturn(updateItemCommand).when(service).buildUpdateAttachedItemCommand(any(), any());
+			doReturn(updateItemCommand).when(service).buildPatchAttachedItemCommand(any(), any());
 
 			service.overrideAttachedItem(AttachedItemTestFactory.createBescheid(), command);
 
@@ -467,6 +692,62 @@ class AttachedItemServiceTest {
 		}
 	}
 
+	@DisplayName("Build map with all bescheid fields")
+	@Nested
+	class TestBuildMapWithAllBescheidFields {
+
+		private final Command command = CommandTestFactory.createBuilder().bodyObject(AttachedItemTestFactory.createBescheidItem()).build();
+
+		@Test
+		void shouldHaveSetBescheidenAm() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_BESCHIEDEN_AM, AttachedItemTestFactory.BESCHEIDEN_AM);
+		}
+
+		@Test
+		void shouldHaveSetBewilligt() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_BEWILLIGT, AttachedItemTestFactory.BEWILLIGT);
+		}
+
+		@Test
+		void shouldHaveSetBescheidDocument() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_BESCHEID_DOCUMENT, AttachedItemTestFactory.BESCHEID_DOCUMENT);
+		}
+
+		@Test
+		void shouldHaveSetSendBy() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_SEND_BY, AttachedItemTestFactory.SEND_BY.name());
+		}
+
+		@Test
+		void shouldHaveSetNachrichtText() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_NACHRICHT_TEXT, AttachedItemTestFactory.NACHRICHT_TEXT);
+		}
+
+		@Test
+		void shouldHaveSetNachrichtSubject() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_NACHRICHT_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT);
+		}
+
+		@Test
+		void shouldHaveSetAttachments() {
+			var bescheidItem = service.buildItemMapWithAllBescheidFields(command);
+
+			assertThat(bescheidItem).containsEntry(Bescheid.FIELD_ATTACHMENTS, List.of(AttachedItemTestFactory.ATTACHMENT));
+		}
+	}
+
 	@Nested
 	class TestAddValueFromMapIfExists {
 
@@ -572,7 +853,7 @@ class AttachedItemServiceTest {
 
 		@Test
 		void shouldCallVorgangIdMapper() {
-			service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
 			verify(commandMapper).toOzgCloudVorgangId(CommandTestFactory.VORGANG_ID);
 		}
@@ -582,14 +863,14 @@ class AttachedItemServiceTest {
 			var expectedVorgangId = OzgCloudVorgangId.from(CommandTestFactory.VORGANG_ID);
 			when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedVorgangId);
 
-			var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
 			assertThat(result.getVorgangId()).isEqualTo(expectedVorgangId);
 		}
 
 		@Test
 		void shouldCallRelationIdMapper() {
-			service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
 			verify(commandMapper).mapRelationId(AttachedItemTestFactory.ID);
 		}
@@ -599,30 +880,30 @@ class AttachedItemServiceTest {
 			var expectedId = GenericId.from("relationId");
 			when(commandMapper.mapRelationId(any())).thenReturn(expectedId);
 
-			var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
 			assertThat(result.getRelationId()).isEqualTo(expectedId);
 		}
 
 		@Test
 		void shouldSetRelationVersion() {
-			var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
 			assertThat(result.getRelationVersion()).isEqualTo(AttachedItemTestFactory.VERSION);
 		}
 
 		@Test
 		void shouldSetOrder() {
-			var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
+			var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of());
 
-			assertThat(result.getOrder()).isEqualTo(AttachedItemService.UPDATE_ATTACHED_ITEM_ORDER);
+			assertThat(result.getOrder()).isEqualTo(AttachedItemService.PATCH_ATTACHED_ITEM);
 		}
 
 		@Test
 		void shouldSetBodyObject() {
 			var bodyObject = Map.<String, Object>of("key", "value");
 
-			var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), bodyObject);
+			var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), bodyObject);
 
 			assertThat(result.getBodyObject()).containsAllEntriesOf(bodyObject);
 		}
@@ -639,17 +920,17 @@ class AttachedItemServiceTest {
 
 		@Test
 		void shouldCallGetBescheid() {
-			when(remoteService.getBescheid(any())).thenReturn(AttachedItemTestFactory.createBescheid());
+			when(remoteService.getItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
 
 			deleteBescheidDraft();
 
-			verify(remoteService).getBescheid(AttachedItemTestFactory.ID);
+			verify(remoteService).getItem(AttachedItemTestFactory.ID);
 		}
 
 		@Test
 		void shouldCallCommandService() {
 			doReturn(deleteItemOzgCloudCommand).when(service).buildDeleteItemCommand(any(), any());
-			when(remoteService.getBescheid(any())).thenReturn(AttachedItemTestFactory.createBescheid());
+			when(remoteService.getItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
 
 			deleteBescheidDraft();
 
@@ -658,7 +939,7 @@ class AttachedItemServiceTest {
 
 		@Test
 		void shouldThrowExceptionIfBescheidNotFound() {
-			when(remoteService.getBescheid(any())).thenThrow(StatusRuntimeException.class);
+			when(remoteService.getItem(any())).thenThrow(StatusRuntimeException.class);
 
 			assertThrows(StatusRuntimeException.class, this::deleteBescheidDraft);
 		}
@@ -730,8 +1011,7 @@ class AttachedItemServiceTest {
 
 		@Test
 		void shouldThrowExceptionIfBescheidIsNotDraft() {
-			var bescheidItem =
-					AttachedItemTestFactory.createBescheidBuilder().clearItem().itemEntry(Bescheid.FIELD_STATUS, "otherStatus").build();
+			var bescheidItem = AttachedItemTestFactory.createBescheidBuilder().clearItem().itemEntry(Bescheid.FIELD_STATUS, "otherStatus").build();
 
 			assertThrows(TechnicalException.class, () -> service.validateBescheidStatus(bescheidItem));
 		}
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
index 8f95f2f61208814a994c88d72ce767b4ff11879b..201fd3f6444931b06f9c1ffec5baab3a90771a6c 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemTestFactory.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemTestFactory.java
@@ -44,6 +44,7 @@ public class AttachedItemTestFactory {
 	public static final String CLIENT = "client";
 
 	public static final String BESCHEIDEN_AM = "2024-01-01";
+	public static final boolean BEWILLIGT = true;
 	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;
@@ -64,7 +65,7 @@ public class AttachedItemTestFactory {
 		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_BEWILLIGT, BEWILLIGT);
 		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());
@@ -72,8 +73,7 @@ public class AttachedItemTestFactory {
 		item.put(Bescheid.FIELD_NACHRICHT_SUBJECT, NACHRICHT_SUBJECT);
 		item.put(Bescheid.FIELD_SENT_INFO, Map.of(
 				Bescheid.FIELD_SENT_BY, SENT_BY,
-				Bescheid.FIELD_SENT_AT, SENT_AT_STR)
-		);
+				Bescheid.FIELD_SENT_AT, SENT_AT_STR));
 		return item;
 	}
 
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 11779af655705946b71d382902ce71b3c97118e6..0fc0a06502b2896cdfb67125649c1ede7c047926 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,6 +26,7 @@
 package de.ozgcloud.bescheid.attacheditem;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.util.Collections;
@@ -347,7 +348,7 @@ class VorgangAttachedItemRemoteServiceTest {
 		void shouldCallGetServiceStab() {
 			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
 
-			getBescheid();
+			getItem();
 
 			verify(service).getServiceStub();
 		}
@@ -356,7 +357,7 @@ class VorgangAttachedItemRemoteServiceTest {
 		void shouldCallBuildRequest() {
 			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
 
-			getBescheid();
+			getItem();
 
 			verify(service).buildGetByIdRequest(AttachedItemTestFactory.ID);
 		}
@@ -366,7 +367,7 @@ class VorgangAttachedItemRemoteServiceTest {
 			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
 			doReturn(grpcVorgangAttachedItemRequest).when(service).buildGetByIdRequest(any());
 
-			getBescheid();
+			getItem();
 
 			verify(serviceStub).getById(grpcVorgangAttachedItemRequest);
 		}
@@ -375,7 +376,7 @@ class VorgangAttachedItemRemoteServiceTest {
 		void shouldCallMapper() {
 			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
 
-			getBescheid();
+			getItem();
 
 			verify(attachedItemMapper).mapFromVorgangAttachedItem(grpcVorgangAttachedItem);
 		}
@@ -386,13 +387,13 @@ class VorgangAttachedItemRemoteServiceTest {
 			when(attachedItemMapper.mapFromVorgangAttachedItem(any())).thenReturn(expectedBescheid);
 			when(grpcVorgangAttachedItemResponse.getVorgangAttachedItem()).thenReturn(grpcVorgangAttachedItem);
 
-			var result = getBescheid();
+			var result = getItem();
 
 			assertThat(result).isEqualTo(expectedBescheid);
 		}
 
-		private AttachedItem getBescheid() {
-			return service.getBescheid(AttachedItemTestFactory.ID);
+		private AttachedItem getItem() {
+			return service.getItem(AttachedItemTestFactory.ID);
 		}
 	}
 
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..61873f64571613f97a8a55b58cb23c7d4751a2f4
--- /dev/null
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java
@@ -0,0 +1,45 @@
+package de.ozgcloud.bescheid.common.freemarker;
+
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
+import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
+import de.ozgcloud.common.test.ITCase;
+
+@ITCase
+class TemplateHandlerITCase {
+
+	private static final String BESCHEID_TEMPLATE = "dummy.template.txt.ftlh";
+
+	@Autowired
+	private TemplateHandler handler;
+	@MockBean
+	private BuildProperties properties;
+	@MockBean
+	private OzgCloudCommandService commandService;
+	@MockBean
+	private CommandMapper commandMapper;
+
+	@DisplayName("Get raw template")
+	@Nested
+	class TestGetRawTemplate {
+
+		@Test
+		void shouldReturnTemplateAsString() {
+			var content = handler.getRawTemplate(BESCHEID_TEMPLATE);
+
+			assertThat(content)
+					.contains("Sehr geehrte/r Antragsteller/in")
+					.contains("im folgenden wird evtl was ersetzt")
+					.contains("valueToReplace")
+					.contains("Mit freundlichen Grüßen");
+		}
+	}
+}
diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java
index 5bb7b63531909383162dd11ddcfdaf02949f7d73..e0280020426fe92f6dce27b0f223738f57e108b5 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java
@@ -2,8 +2,7 @@ package de.ozgcloud.bescheid.nachricht;
 
 import static org.assertj.core.api.Assertions.*;
 
-import java.util.Optional;
-
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +26,7 @@ class NachrichtServiceITCase {
 	@MockBean
 	private BuildProperties buildProperties;
 
+	@DisplayName("Build message")
 	@Nested
 	class TestBuildMessage {
 		@Test
@@ -35,20 +35,5 @@ class NachrichtServiceITCase {
 
 			assertThat(message).isNotBlank();
 		}
-
-		@Test
-		void shouldBeGenehmigt() {
-			var message = service.buildMessage(BescheidTestFactory.createBuilder().genehmigt(true).nachrichtText(Optional.empty()).build());
-
-			assertThat(message).contains("genehmigt").doesNotContain("abgelehnt");
-		}
-
-		@Test
-		void shouldBeAbgelehnt() {
-			var message = service.buildMessage(BescheidTestFactory.createBuilder().genehmigt(false).nachrichtText(Optional.empty()).build());
-
-			assertThat(message).contains("abgelehnt").doesNotContain("genehmigt");
-		}
 	}
-
-}
+}
\ 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 777babc893515dec6ddf8f32fcfd030a83e9aa5c..f18ffbc9d6851d39b9dfe8676d98f7676013dbe5 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
@@ -25,6 +25,7 @@ import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId;
 import de.ozgcloud.bescheid.Bescheid;
 import de.ozgcloud.bescheid.BescheidTestFactory;
 import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory;
+import de.ozgcloud.bescheid.common.freemarker.TemplateHandler;
 import de.ozgcloud.bescheid.vorgang.PostfachAddressTestFactory;
 import de.ozgcloud.bescheid.vorgang.ServiceKontoTestFactory;
 import de.ozgcloud.bescheid.vorgang.Vorgang;
@@ -43,6 +44,8 @@ class NachrichtServiceTest {
 	private OzgCloudCommandService commandService;
 	@Mock
 	private CommandMapper commandMapper;
+	@Mock
+	private TemplateHandler templateHandler;
 
 	@Nested
 	class TestCreateNachrichtDraft {
@@ -116,7 +119,7 @@ class NachrichtServiceTest {
 
 		@Test
 		void shouldUseDefaultTemplate() {
-			doReturn("FROM_TEMPLATE").when(service).fillTemplate(any(), any());
+			doReturn("FROM_TEMPLATE").when(templateHandler).fillTemplate(any(), any());
 
 			var message = service.buildMessage(BescheidTestFactory.createBuilder().nachrichtText(Optional.empty()).build());
 
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 9589ada3b530def97b9876347110100bf7f788a7..d9666c6f3edcccc48c5ce830547086ab2f9f8d30 100644
--- a/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java
+++ b/bescheid-manager/src/test/java/de/ozgcloud/document/DocumentServiceTest.java
@@ -25,6 +25,7 @@ package de.ozgcloud.document;
 
 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.Map;
@@ -197,23 +198,23 @@ class DocumentServiceTest {
 
 		@Test
 		void shouldCallGetBescheidItem() {
-			when(attachedItemService.getBescheidItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
+			when(attachedItemService.getItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
 
 			validateBescheidItem();
 
-			verify(attachedItemService).getBescheidItem(AttachedItemTestFactory.ID);
+			verify(attachedItemService).getItem(AttachedItemTestFactory.ID);
 		}
 
 		@Test
 		void shouldThrowExceptionWhenNoBescheidDraft() {
-			when(attachedItemService.getBescheidItem(any())).thenThrow(ResponseStatusException.class);
+			when(attachedItemService.getItem(any())).thenThrow(ResponseStatusException.class);
 
 			assertThrows(ResponseStatusException.class, this::validateBescheidItem);
 		}
 
 		@Test
 		void shouldNotThrowExceptionIfNotDraft() {
-			when(attachedItemService.getBescheidItem(any())).thenReturn(
+			when(attachedItemService.getItem(any())).thenReturn(
 					AttachedItemTestFactory.createBescheidBuilder().clearItem().itemEntry(Bescheid.FIELD_STATUS, "not-draft").build());
 
 			assertThrows(TechnicalException.class, this::validateBescheidItem);
@@ -221,7 +222,7 @@ class DocumentServiceTest {
 
 		@Test
 		void shouldValidateBescheidItem() {
-			when(attachedItemService.getBescheidItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
+			when(attachedItemService.getItem(any())).thenReturn(AttachedItemTestFactory.createBescheid());
 
 			assertDoesNotThrow(this::validateBescheidItem);
 		}
@@ -425,7 +426,7 @@ class DocumentServiceTest {
 
 		@Test
 		void shouldCallDocumentMapper() {
-			var expectedItem  = AttachedItemTestFactory.createDocument();
+			var expectedItem = AttachedItemTestFactory.createDocument();
 			when(attachedItemService.getItem(any())).thenReturn(expectedItem);
 
 			service.getDocument(AttachedItemTestFactory.ID);
diff --git a/bescheid-manager/src/test/resources/templates/dummy.template.txt.ftlh b/bescheid-manager/src/test/resources/templates/dummy.template.txt.ftlh
new file mode 100644
index 0000000000000000000000000000000000000000..9182e3f004e22d03b94e458faac04d2612efee18
--- /dev/null
+++ b/bescheid-manager/src/test/resources/templates/dummy.template.txt.ftlh
@@ -0,0 +1,6 @@
+Sehr geehrte/r Antragsteller/in,
+im folgenden wird evtl was ersetzt
+
+<#if domatch>replacedValue<#else>valueToReplace</#if>
+
+Mit freundlichen Grüßen
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6a91d9c38e7febade4b701411cd8dba92c88b113..1b9b61485236e8343cc4790763c73dde40c7512f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.ozgcloud.vorgang</groupId>
 	<artifactId>vorgang-manager</artifactId>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 
 	<name>OZG-Cloud Vorgang Manager</name>
 	<packaging>pom</packaging>
diff --git a/src/main/helm/templates/_helpers.tpl b/src/main/helm/templates/_helpers.tpl
index 7c94e6cfd9851532d35296171f68cff8ba0e46b2..0b1337c8ab4554f6f66827c60f7113b8d6863d86 100644
--- a/src/main/helm/templates/_helpers.tpl
+++ b/src/main/helm/templates/_helpers.tpl
@@ -101,4 +101,26 @@ app.kubernetes.io/namespace: {{ include "app.namespace" . }}
 {{- $customList = append $customList (dict "name" $key "value" $value) }}
 {{- end -}}
 {{- $customList | toYaml -}}
-{{- end -}}
\ No newline at end of file
+{{- end -}}
+
+
+{{- define "app.bayernidAbsenderName" -}}
+{{- quote (required "ozgcloud.bayernid.absender.name must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).name) -}} 
+{{- end -}}
+
+{{- define "app.bayernidAbsenderMandant" -}}
+{{- if (((.Values.ozgcloud).bayernid).absender).mandant -}}
+{{ quote .Values.ozgcloud.bayernid.absender.mandant }}
+{{- else -}}
+{{ include "app.bayernidAbsenderName" . }}
+{{- end -}}
+{{- end -}}
+
+{{- define "app.bayernidAbsenderDienst" -}}
+{{- if (((.Values.ozgcloud).bayernid).absender).dienst -}}
+{{  quote .Values.ozgcloud.bayernid.absender.dienst }}
+{{- else -}}
+{{ include "app.bayernidAbsenderName" . }}
+{{- end -}}
+{{- end -}}
+
diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml
index 938946431b8c929bd0bd636f933072eedbe44fca..cbb3ae56d5105372f34de651e87da7b08d689402 100644
--- a/src/main/helm/templates/deployment.yaml
+++ b/src/main/helm/templates/deployment.yaml
@@ -137,6 +137,10 @@ spec:
           - name: spring_ssl_bundle_pem_es-root-ca_truststore_certificate
             value: "/bindings/ca-certificates/es-root-ca.pem"
           {{- end }}
+          {{- if ((.Values.ozgcloud).mongodbsearch).enabled }}
+          - name: ozgcloud_mongodbsearch_enabled
+            value: {{ quote .Values.ozgcloud.mongodbsearch.enabled }}
+          {{- end }}
           {{- with include "app.getCustomList" . }}
 {{ . | indent 10 }}
           {{- end }}
@@ -170,17 +174,27 @@ spec:
           - name: grpc_client_bayern-id_negotiationType
             value: {{ (((.Values.ozgcloud).bayernid).proxy).negotiationType | default "PLAINTEXT" }}
           - name: ozgcloud_bayernid_absender_name
-            value: {{ quote (required "ozgcloud.bayernid.absender.name must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).name) }}
+            value: {{ include "app.bayernidAbsenderName" . }}
           - name: ozgcloud_bayernid_absender_anschrift
             value: {{ quote (required "ozgcloud.bayernid.absender.anschrift must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).anschrift) }}
           - name: ozgcloud_bayernid_absender_dienst
-            value: {{ quote (required "ozgcloud.bayernid.absender.dienst must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).dienst) }}
+            value: {{ include "app.bayernidAbsenderDienst" . }}
           - name: ozgcloud_bayernid_absender_mandant
-            value: {{ quote (required "ozgcloud.bayernid.absender.mandant must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).mandant) }}
+            value: {{ include "app.bayernidAbsenderMandant" . }}
           - 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).muk).enabled }}
+          - name: ozgcloud_muk_sender
+            value: {{ quote (required "ozgcloud.muk.sender must be set if ozgcloud.muk is enabled" ((.Values.ozgcloud).muk).sender) }}
+          - name: ozgcloud_muk_server
+            value: {{ quote (required "ozgcloud.muk.server must be set if ozgcloud.muk is enabled" ((.Values.ozgcloud).muk).server) }}
+          {{- end }}
           
+
+
+
           {{- if ((.Values.ozgcloud).antragraum).enabled }}
           - name: ozgcloud_antragraum_enabled
             value: {{ quote .Values.ozgcloud.antragraum.enabled }}
diff --git a/src/test/helm/deployment_bayernid_test.yaml b/src/test/helm/deployment_bayernid_test.yaml
index 2a73c2761d442304840198966a1641a74081124a..5b4837870b547f1c80384011bbf8dce8305eacc4 100644
--- a/src/test/helm/deployment_bayernid_test.yaml
+++ b/src/test/helm/deployment_bayernid_test.yaml
@@ -42,10 +42,8 @@ tests:
             address: https://proxy.address.local
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
-            dienst: "dienst"
-            mandant: "mandant"
+            name: "name"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
       - contains:
@@ -66,18 +64,18 @@ tests:
       - contains:
           path: spec.template.spec.containers[0].env
           content:
-            name: ozgcloud_bayernid_absender_dienst
-            value: "dienst"
+            name: ozgcloud_bayernid_absender_gemeindeSchluessel
+            value: "gemeindeSchluessel"
       - contains:
           path: spec.template.spec.containers[0].env
           content:
-            name: ozgcloud_bayernid_absender_mandant
-            value: "mandant"
+            name: ozgcloud_bayernid_absender_dienst
+            value: "name"
       - contains:
           path: spec.template.spec.containers[0].env
           content:
-            name: ozgcloud_bayernid_absender_gemeindeSchluessel
-            value: "gemeindeSchluessel"
+            name: ozgcloud_bayernid_absender_mandant
+            value: "name"
       - contains:
           path: spec.template.spec.containers[0].env
           content:
@@ -99,8 +97,6 @@ tests:
           absender:
             postkorbId: "postkorbId"
             anschrift: "anschrift"
-            dienst: "dienst"
-            mandant: "mandant"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
       - failedTemplate:
@@ -115,15 +111,14 @@ tests:
             address: https://proxy.address.local
           absender:
             postkorbId: "postkorbId"
-            name: "name"
-            dienst: "dienst"
-            mandant: "mandant"
+            name: "name_dienst_mandant"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
       - failedTemplate:
           errorMessage: "ozgcloud.bayernid.absender.anschrift must be set if ozgcloud.bayernid is enabled"
 
-  - it: should fail if absender dienst is not set
+
+  - it: should fail if absender gemeindeSchluessel is not set
     set:
       ozgcloud:
         bayernid:
@@ -132,82 +127,86 @@ tests:
             address: https://proxy.address.local
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
-            mandant: "mandant"
-            gemeindeSchluessel: "gemeindeSchluessel"
+            name: "name_dienst_mandant"
     asserts:
       - failedTemplate:
-          errorMessage: "ozgcloud.bayernid.absender.dienst must be set if ozgcloud.bayernid is enabled"
+          errorMessage: "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled"
 
-  - it: should fail if absender mandant is not set
+  - it: should fail if bayernid proxy is enabled but proxy address is not configured
     set:
       ozgcloud:
         bayernid:
           enabled: true
-          proxy:
-            address: https://proxy.address.local
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
-            dienst: "dienst"
+            name: "name_dienst_mandant"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
       - failedTemplate:
-          errorMessage: "ozgcloud.bayernid.absender.mandant must be set if ozgcloud.bayernid is enabled"
+          errorMessage: "ozgcloud.bayernid.proxy.address must be set if ozgcloud.bayernid is enabled"
 
-  - it: should fail if absender gemeindeSchluessel is not set
+  - it: should set the bayernid proxy grpc negotiationType
     set:
       ozgcloud:
         bayernid:
           enabled: true
           proxy:
             address: https://proxy.address.local
+            negotiationType: NOT_DEFAULT
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
-            dienst: "dienst"
-            mandant: "mandant"
+            name: "name_dienst_mandant"
+            gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
-      - failedTemplate:
-          errorMessage: "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled"
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: grpc_client_bayern-id_negotiationType
+            value: NOT_DEFAULT
 
-  - it: should fail if bayernid proxy is enabled but proxy address is not configured
+  - it: should overwrite with absender name if mandant is set
     set:
       ozgcloud:
         bayernid:
           enabled: true
+          proxy:
+            address: https://proxy.address.local
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
-            dienst: "dienst"
+            name: "name"
             mandant: "mandant"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
-      - failedTemplate:
-          errorMessage: "ozgcloud.bayernid.proxy.address must be set if ozgcloud.bayernid is enabled"
-
-  - it: should set the bayernid proxy grpc negotiationType
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_bayernid_enabled
+            value: "true"
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_bayernid_absender_mandant
+            value: "mandant"
+  - it: should overwrite with absender name if dienst is set
     set:
       ozgcloud:
         bayernid:
           enabled: true
           proxy:
             address: https://proxy.address.local
-            negotiationType: NOT_DEFAULT
           absender:
             postkorbId: "postkorbId"
-            name: "name"
             anschrift: "anschrift"
+            name: "name"
             dienst: "dienst"
-            mandant: "mandant"
             gemeindeSchluessel: "gemeindeSchluessel"
     asserts:
       - contains:
           path: spec.template.spec.containers[0].env
           content:
-            name: grpc_client_bayern-id_negotiationType
-            value: NOT_DEFAULT
+            name: ozgcloud_bayernid_absender_dienst
+            value: "dienst"
diff --git a/src/test/helm/deployment_mongodb_test.yaml b/src/test/helm/deployment_mongodb_test.yaml
index 7daf5d3196bd7c34413c8aff2741a9159c3a1fdd..ad8a4cb17c47cdca0b5cc89daf2055ec9a24109e 100644
--- a/src/test/helm/deployment_mongodb_test.yaml
+++ b/src/test/helm/deployment_mongodb_test.yaml
@@ -93,3 +93,21 @@ tests:
           content:
             name: spring_data_mongodb_database
             value: vorgang-manager-database
+
+  - it: check mongodb full text search disabled
+    asserts:
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_mongodbsearch_enabled
+            value: "true"
+
+  - it: check mongodb full text search enabled
+    set:
+      ozgcloud.mongodbsearch.enabled: true
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_mongodbsearch_enabled
+            value: "true"
diff --git a/src/test/helm/deployment_muk_test.yaml b/src/test/helm/deployment_muk_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..33c02db5fd14acf174884dc8f09517d0403bf1b1
--- /dev/null
+++ b/src/test/helm/deployment_muk_test.yaml
@@ -0,0 +1,89 @@
+#
+# Copyright (C) 2023 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 muk
+release:
+  name: vorgang-manager
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-pull-secret
+tests:
+  - it: should set muk values
+    set:
+      ozgcloud:
+        muk:
+          enabled: true
+          server: muk.test.ozg.de
+          sender: "name"
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_muk_sender
+            value: "name"
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_muk_server
+            value: muk.test.ozg.de
+  - it: should not by default set muk values
+    asserts:
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_muk_sender
+          any: true
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_muk_server
+          any: true
+       
+  - it: should fail if sender name is not set
+    set:
+      ozgcloud:
+        muk:
+          enabled: true
+          server: muk.test.ozg.de  
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.muk.sender must be set if ozgcloud.muk is enabled"
+
+
+  - it: should fail if muk server is not set
+    set:
+      ozgcloud:
+        muk:
+          enabled: true
+          sender: "name"
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.muk.server must be set if ozgcloud.muk is enabled"
+
+
+
diff --git a/vorgang-manager-base/pom.xml b/vorgang-manager-base/pom.xml
index 222297d76dd354d3d4e3eb0c88d00b694d316225..01a891cafa5c98d4a5bf87091817403b5c6a9f88 100644
--- a/vorgang-manager-base/pom.xml
+++ b/vorgang-manager-base/pom.xml
@@ -6,13 +6,13 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.2.0</version>
+		<version>4.3.0</version>
 		<relativePath />
 	</parent>
 
 	<groupId>de.ozgcloud.vorgang</groupId>
 	<artifactId>vorgang-manager-base</artifactId>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 
 	<name>OZG-Cloud Vorgang Manager Base</name>
 
diff --git a/vorgang-manager-command/pom.xml b/vorgang-manager-command/pom.xml
index 0fbbe6fad1054675ef511d164b91eada693b3b07..1427d6e703f39962af7b6136e3319e01c87022e2 100644
--- a/vorgang-manager-command/pom.xml
+++ b/vorgang-manager-command/pom.xml
@@ -4,13 +4,13 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-dependencies</artifactId>
-		<version>4.2.0</version>
+		<version>4.3.0</version>
 		<relativePath/>
 	</parent>
 	
 	<groupId>de.ozgcloud.command</groupId>
 	<artifactId>command-manager</artifactId>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 	<name>OZG-Cloud Command Manager</name>
 
 	<properties>
diff --git a/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java b/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java
index 926ca62da79c8ac64c56bc9fd4d9f0fc59e84aea..31bbfd577e335865cdebef9b37de0c5613c52f18 100644
--- a/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java
+++ b/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java
@@ -24,5 +24,5 @@
 package de.ozgcloud.command;
 
 public enum CommandStatus {
-	PENDING, FINISHED, ERROR, REVOKE_PENDING, REVOKED;
+	NEW, PENDING, CANCELED, FINISHED, ERROR, REVOKE_PENDING, REVOKED
 }
\ No newline at end of file
diff --git a/vorgang-manager-command/src/test/java/de/ozgcloud/command/CommandTestFactory.java b/vorgang-manager-command/src/test/java/de/ozgcloud/command/CommandTestFactory.java
index 67e86232e49882c6fc469d07fbb509b52943ca6d..886d82f62b9d791d35214ff718c45cdac71bb2ee 100644
--- a/vorgang-manager-command/src/test/java/de/ozgcloud/command/CommandTestFactory.java
+++ b/vorgang-manager-command/src/test/java/de/ozgcloud/command/CommandTestFactory.java
@@ -1,12 +1,13 @@
 package de.ozgcloud.command;
 
-import java.util.Map;
+import java.util.Collections;
 import java.util.UUID;
 
 public class CommandTestFactory {
 
 	public static final String ID = UUID.randomUUID().toString();
 	public static final String VORGANG_ID = UUID.randomUUID().toString();
+	public static final String RELATION_ID = UUID.randomUUID().toString();
 	public static final String ORDER = "DO_TEST";
 
 	public static final String CREATED_BY = UUID.randomUUID().toString();
@@ -18,9 +19,10 @@ public class CommandTestFactory {
 	public static TestCommand.TestCommandBuilder createBuilder() {
 		return TestCommand.builder()
 				.id(ID)
+				.relationId(RELATION_ID)
 				.vorgangId(VORGANG_ID)
-				.body(Map.of())
-				.bodyObject(Map.of())
+				.body(Collections.emptyMap())
+				.bodyObject(Collections.emptyMap())
 				.createdBy(CREATED_BY);
 	}
 }
diff --git a/vorgang-manager-interface/pom.xml b/vorgang-manager-interface/pom.xml
index f3c97b987a7ae006827201ebb600d4c6f7228b05..c64435edbd2a453b7b75be55b0647d46ac6d644b 100644
--- a/vorgang-manager-interface/pom.xml
+++ b/vorgang-manager-interface/pom.xml
@@ -30,13 +30,13 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-dependencies</artifactId>
-		<version>4.2.0</version>
+		<version>4.3.0</version>
 		<relativePath/>
 	</parent>
 
 	<groupId>de.ozgcloud.vorgang</groupId>
 	<artifactId>vorgang-manager-interface</artifactId>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 
 	<name>OZG-Cloud Vorgang Manager gRPC Interface</name>
 	<description>Interface (gRPC) for Vorgang Manager Server</description>
diff --git a/vorgang-manager-server/pom.xml b/vorgang-manager-server/pom.xml
index f70af29d7f58dd9a5083f38c622ea640bb55da76..ec7bfbbe029cab1c6d2c88820de252b3cec29905 100644
--- a/vorgang-manager-server/pom.xml
+++ b/vorgang-manager-server/pom.xml
@@ -32,13 +32,13 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.3.0-SNAPSHOT</version>
+		<version>4.3.0</version>
 		<relativePath />
 	</parent>
 
 	<groupId>de.ozgcloud.vorgang</groupId>
 	<artifactId>vorgang-manager-server</artifactId>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 
 	<name>OZG-Cloud Vorgang Manager Server</name>
 	<description>Server Implementierung des VorgangManagers</description>
@@ -53,11 +53,11 @@
 		<zufi-manager-interface.version>1.0.0-SNAPSHOT</zufi-manager-interface.version>
 
 		<user-manager-interface.version>2.1.0</user-manager-interface.version>
-		<bescheid-manager.version>1.13.0-SNAPSHOT</bescheid-manager.version>
-		<processor-manager.version>0.4.0</processor-manager.version>
-		<nachrichten-manager.version>2.9.0-SNAPSHOT</nachrichten-manager.version>
-		<ozgcloud-starter.version>0.10.0-SNAPSHOT</ozgcloud-starter.version>
-		<notification-manager.version>2.8.0-SNAPSHOT</notification-manager.version>
+		<bescheid-manager.version>1.14.0</bescheid-manager.version>
+		<processor-manager.version>0.4.1</processor-manager.version>
+		<nachrichten-manager.version>2.9.0</nachrichten-manager.version>
+		<ozgcloud-starter.version>0.10.0</ozgcloud-starter.version>
+		<notification-manager.version>2.8.0</notification-manager.version>
 
 		<zip.version>2.11.1</zip.version>
 		<jsoup.version>1.15.3</jsoup.version>
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java
index 62811097900c5e6daee5d06831962184579d63b8..3fe994644fae00480fdda6814cae0dcbec5abb5a 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java
@@ -31,6 +31,7 @@ import org.springframework.stereotype.Component;
 import de.ozgcloud.apilib.common.callcontext.CallContext;
 import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider;
 import de.ozgcloud.apilib.user.OzgCloudUserId;
+import de.ozgcloud.vorgang.callcontext.CallContextUser;
 import de.ozgcloud.vorgang.callcontext.CurrentUserService;
 import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor;
 import lombok.RequiredArgsConstructor;
@@ -45,11 +46,11 @@ public class VorgangManagerCallContextProvider implements OzgCloudCallContextPro
 	@Override
 	public CallContext provideContext() {
 		var callContextBuilder = CallContext.builder().clientName(VorgangManagerClientCallContextAttachingInterceptor.VORGANG_MANAGER_CLIENT_NAME);
-		getUserId().ifPresent(callContextBuilder::userId);
+		findUserId().ifPresent(callContextBuilder::userId);
 		return callContextBuilder.build();
 	}
 
-	Optional<OzgCloudUserId> getUserId() {
-		return currentUserService.getUser().getUserId().map(OzgCloudUserId::from);
+	Optional<OzgCloudUserId> findUserId() {
+		return currentUserService.findUser().flatMap(CallContextUser::getUserId).map(OzgCloudUserId::from);
 	}
 }
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java
index 56d602a31ebc0a372e80cb958e604f85c5e7fef1..9fdab580da21c2a57572b28612b428f8a198fbd7 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java
@@ -7,29 +7,15 @@ import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider;
 import de.ozgcloud.apilib.user.GrpcOzgCloudUserProfileService;
 import de.ozgcloud.apilib.user.OzgCloudUserProfileService;
 import de.ozgcloud.apilib.user.UserProfileMapper;
-import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService;
-import de.ozgcloud.apilib.vorgang.grpc.GrpcOzgCloudVorgangService;
-import de.ozgcloud.apilib.vorgang.grpc.OzgCloudVorgangMapper;
-import de.ozgcloud.apilib.vorgang.grpc.OzgCloudVorgangStubMapper;
 import de.ozgcloud.user.grpc.userprofile.UserProfileServiceGrpc.UserProfileServiceBlockingStub;
-import de.ozgcloud.vorgang.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub;
 import net.devh.boot.grpc.client.inject.GrpcClient;
 
 @Configuration
 class VorgangProcessorConfiguration {
 
-	@GrpcClient("vorgang-manager")
-	private VorgangServiceBlockingStub vorgangServiceStub;
-
 	@GrpcClient("user-manager")
 	private UserProfileServiceBlockingStub userProfileServiceGrpc;
 
-	@Bean
-	OzgCloudVorgangService ozgCloudVorgangService(OzgCloudVorgangMapper mapper, OzgCloudVorgangStubMapper stubMapper,
-			OzgCloudCallContextProvider contextProvider) {
-		return new GrpcOzgCloudVorgangService(vorgangServiceStub, mapper, stubMapper, contextProvider);
-	}
-
 	@Bean
 	OzgCloudUserProfileService ozgCloudUserProfileService(UserProfileMapper mapper, OzgCloudCallContextProvider contextProvider) {
 		return new GrpcOzgCloudUserProfileService(userProfileServiceGrpc, mapper, contextProvider);
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java
index 60d9b646de80c056a7f4df16a06c69861ab571e8..0fcd73c898e729dc7d06ef4f9f862976e4f60676 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java
@@ -27,6 +27,10 @@ import java.util.Map;
 
 interface VorgangAttachedItemCustomRepository {
 
+	void update(String itemId, long version, Map<String, Object> properyMap);
+
+	void forceUpdate(String itemId, Map<String, Object> propertyMap);
+
 	void patch(String itemId, long version, Map<String, Object> propertyMap);
 
 	void forcePatch(String itemId, Map<String, Object> propertyMap);
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java
index 66c828d24149e338c647d9eff9d1f7e34347c033..c3bb8ce0a775e518f791f245f5d0b3cb7614ad27 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java
@@ -60,14 +60,32 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
 	}
 
 	boolean doVorgangAttachedItemExist(String itemId) {
-		return mongoOperations.exists(query(isId(itemId)), VorgangAttachedItem.COLLECTION_NAME);
+		return mongoOperations.exists(createQueryById(itemId), VorgangAttachedItem.class);
+	}
+
+	@Override
+	public void update(String itemId, long version, Map<String, Object> properyMap) {
+		var updateResult = mongoOperations.updateFirst(query(byIdAndVersion(itemId, version)), buildUpdateForItemAndVersion(properyMap),
+				VorgangAttachedItem.class);
+		collisionVerifier.verify(updateResult, itemId);
+	}
+
+	@Override
+	public void forceUpdate(String itemId, Map<String, Object> propertyMap) {
+		mongoOperations.updateFirst(createQueryById(itemId), buildUpdateForItemAndVersion(propertyMap), VorgangAttachedItem.class);
+	}
+
+	private Update buildUpdateForItemAndVersion(Map<String, Object> propertyMap) {
+		var item = new Document();
+		item.putAll(propertyMap);
+		return new Update().set(VorgangAttachedItem.FIELDNAME_ITEM, item);
 	}
 
 	@Override
 	public void patch(String itemId, long version, Map<String, Object> propertyMap) {
 		mongoOperations.updateFirst(
 				query(byIdAndVersion(itemId, version)),
-				createUpdateForItemAndVersion(propertyMap),
+				createPatchUpdateForItemAndVersion(propertyMap),
 				VorgangAttachedItem.class);
 
 	}
@@ -76,7 +94,7 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
 	public void forcePatch(String itemId, Map<String, Object> propertiesMap) {
 		mongoOperations.updateFirst(
 				createQueryById(itemId),
-				createUpdateForItemAndVersion(propertiesMap),
+				createPatchUpdateForItemAndVersion(propertiesMap),
 				VorgangAttachedItem.class);
 	}
 
@@ -100,7 +118,7 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI
 		return where(VorgangAttachedItem.FIELDNAME_IS_DELETED).is(false);
 	}
 
-	private Update createUpdateForItemAndVersion(Map<String, Object> propertyMap) {
+	private Update createPatchUpdateForItemAndVersion(Map<String, Object> propertyMap) {
 		return Update.fromDocument(buildItemPatchDocument(propertyMap)).inc(VorgangAttachedItem.FIELDNAME_VERSION, 1);
 	}
 
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java
index cf25401a6607aa4468ddad30e456b9f35924904f..2a6d887bac4dd22eae1201d9a1a28f9b64a20732 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java
@@ -74,13 +74,13 @@ public class VorgangAttachedItemService {
 	}
 
 	public void update(String commandId, VorgangAttachedItem item) {
-		repository.patch(item.getId(), item.getVersion(), item.getItem());
+		repository.update(item.getId(), item.getVersion(), item.getItem());
 
 		publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId));
 	}
 
 	public void forceUpdate(String commandId, VorgangAttachedItem item) {
-		repository.forcePatch(item.getId(), item.getItem());
+		repository.forceUpdate(item.getId(), item.getItem());
 
 		publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId));
 	}
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java
index 6222d4569a6002727397d8df8c4a7b185f711219..da2142d42b5c41d75b9b0266bfe04edb2cef21f9 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java
@@ -36,10 +36,17 @@ import java.util.stream.Stream;
 
 import org.apache.commons.collections.MapUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.FindAndModifyOptions;
 import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.ComparisonOperators.Eq;
+import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch;
+import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator;
+import org.springframework.data.mongodb.core.aggregation.SetOperation;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.stereotype.Repository;
 
 import com.google.common.collect.ObjectArrays;
@@ -209,10 +216,6 @@ class CommandRepository {
 		return findOne(query).map(Command::getBodyObject).map(this::getParentIdProperty);
 	}
 
-	private Query queryById(String commandId) {
-		return query(criteriaById(commandId));
-	}
-
 	private Criteria criteriaById(String commandId) {
 		return where(MONGODB_ID).is(commandId);
 	}
@@ -258,4 +261,22 @@ class CommandRepository {
 		query.fields().include(MONGODB_CLASS, MONGODB_ID);
 		return query;
 	}
+
+	public Command setRevokeStatus(String id) {
+		return mongoOperations.findAndModify(queryById(id), buildUpdateStatusRevoke(), FindAndModifyOptions.options().returnNew(true), Command.class);
+	}
+
+	private Query queryById(String commandId) {
+		return query(criteriaById(commandId));
+	}
+
+	private UpdateDefinition buildUpdateStatusRevoke() {
+		var switchOperation = Switch.switchCases(
+				CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.NEW)).then(CommandStatus.CANCELED),
+				CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.PENDING)).then(CommandStatus.REVOKE_PENDING),
+				CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.FINISHED)).then(CommandStatus.REVOKE_PENDING)
+		).defaultTo("$status");
+		return Aggregation.newUpdate(SetOperation.set(MONGODB_STATUS).toValue(switchOperation));
+	}
+
 }
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java
index 9b2e19c7f3172cea9a1dcc813bcc4e37774f7793..aa5c3102a12e16109046248cb4f3b6d34117c8d6 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java
@@ -24,6 +24,7 @@
 package de.ozgcloud.vorgang.command;
 
 import static de.ozgcloud.vorgang.command.PersistedCommand.*;
+import static java.util.Objects.*;
 
 import java.time.ZonedDateTime;
 import java.util.Collection;
@@ -36,7 +37,6 @@ import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
-import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
@@ -109,10 +109,6 @@ public class CommandService {
 		return command;
 	}
 
-	public Optional<Command> findCommand(String commandId) {
-		return repository.getById(commandId);
-	}
-
 	public Stream<Command> findFinishedVorgangLoeschenCommandsOlderThen(@NonNull ZonedDateTime createdAfter) {
 		return repository.findCommands(Order.VORGANG_LOESCHEN, CommandStatus.FINISHED, createdAfter);
 	}
@@ -122,6 +118,11 @@ public class CommandService {
 	}
 
 	public void setCommandFinished(String commandId, String createdResource) {
+		var command = getCommand(commandId);
+		if (command.getStatus() == CommandStatus.REVOKE_PENDING) {
+			publishRevokeCommandEvent(command);
+			return;
+		}
 		Optional.ofNullable(createdResource).ifPresentOrElse(
 				resource -> repository.finishCommand(commandId, resource),
 				() -> repository.finishCommand(commandId));
@@ -150,34 +151,24 @@ public class CommandService {
 	}
 
 	public void setCommandRevoked(String commandId) {
-		updateCommandStatus(commandId, CommandStatus.REVOKED);
-	}
-
-	public void setCommandRevokePending(final String commandId) {
-		updateCommandStatus(commandId, CommandStatus.REVOKE_PENDING);
-
-		publishRevokeCommandEvent(commandId);
+		repository.updateCommandStatus(commandId, CommandStatus.REVOKED);
 	}
 
-	void publishRevokeCommandEvent(final String commandId) {
-		var commandOptional = repository.getById(commandId);
-		commandOptional.ifPresentOrElse(command -> {
-			if (MapUtils.isEmpty(((PersistedCommand) command).getPreviousState())) {
-				handleError(commandId, "Command %s can not be revoked because it has no previous state.");
-			}
-			publisher.publishEvent(new RevokeCommandEvent(command));
-		}, () -> handleError(commandId, "No Command with id %s found."));
+	public Command revokeCommand(final String commandId) {
+		var updatedCommand = setRevokeStatus(commandId);
+		if (isNull(updatedCommand)) {
+			throw new NotFoundException(Command.class, commandId);
+		}
+		publishRevokeCommandEvent(updatedCommand);
+		return updatedCommand;
 	}
 
-	private void handleError(String commandId, String messageTemplate) {
-		var message = messageTemplate.formatted(commandId);
-		LOG.error(message);
-		publisher.publishEvent(new CommandFailedEvent(commandId, message));
-		throw new TechnicalException(message);
+	public Command setRevokeStatus(String commandId) {
+		return repository.setRevokeStatus(commandId);
 	}
 
-	void updateCommandStatus(String commandId, CommandStatus status) {
-		repository.updateCommandStatus(commandId, status);
+	void publishRevokeCommandEvent(Command command) {
+		publisher.publishEvent(new RevokeCommandEvent(command));
 	}
 
 	public boolean existsPendingCommands(String vorgangId) {
@@ -212,13 +203,21 @@ public class CommandService {
 	}
 
 	Command getNotFailedCommand(String commandId) {
-		var command = findCommand(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId));
+		var command = getCommand(commandId);
 		if (command.getStatus() == CommandStatus.ERROR) {
 			throw new TechnicalException("Cannot add sub commands to failed command ('%s')".formatted(commandId));
 		}
 		return command;
 	}
 
+	public Command getCommand(String commandId) {
+		return findCommand(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId));
+	}
+
+	public Optional<Command> findCommand(String commandId) {
+		return repository.getById(commandId);
+	}
+
 	Map<String, Object> buildSubCommandValues(CreateSubCommandsRequest request) {
 		var executionMode = request.getExecutionMode();
 		return Map.of(
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java
index 4237a4c2f986161b0b76880b2aa50409e751fd68..2a63348c24d5b4370aaa94c0a16f3ca7dc91c167 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java
@@ -36,7 +36,6 @@ import org.springframework.context.ApplicationEventPublisher;
 import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandExecutedEvent;
 import de.ozgcloud.command.CommandStatus;
-import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.vorgang.command.CommandResponse.ResponseCode;
 import de.ozgcloud.vorgang.common.errorhandling.NotFoundException;
 import de.ozgcloud.vorgang.common.security.PolicyService;
@@ -92,11 +91,6 @@ public class GrpcCommandService extends CommandServiceImplBase {
 		responseObserver.onCompleted();
 	}
 
-	// TODO check - is this function still in use?
-	boolean isStatusChangeOrder(String order) {
-		return order.startsWith("VORGANG");
-	}
-
 	@Override
 	public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> responseObserver) {
 		policyService.checkPermissionByCommand(request.getId());
@@ -110,19 +104,9 @@ public class GrpcCommandService extends CommandServiceImplBase {
 	@Override
 	public void revokeCommand(GrpcRevokeCommandRequest request, StreamObserver<GrpcCommandResponse> responseObserver) {
 		policyService.checkPermissionByCommand(request.getId());
-
-		String commandId = request.getId();
-
-		try {
-			commandService.setCommandRevokePending(commandId);
-
-			Command updatedCommand = getCommand(commandId);
-
-			responseObserver.onNext(mapToGrpcResponse(updatedCommand));
-			responseObserver.onCompleted();
-		} catch (TechnicalException e) {
-			responseObserver.onError(e);
-		}
+		var updatedCommand = commandService.revokeCommand(request.getId());
+		responseObserver.onNext(mapToGrpcResponse(updatedCommand));
+		responseObserver.onCompleted();
 	}
 
 	Command getCommand(String commandId) {
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java
index d5a1c169125ab7a84e1d734ad6e85f6adf5adccf..11318a0cf33d16cc95296cef55b61c1f08a878b8 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java
@@ -41,11 +41,14 @@ import net.devh.boot.grpc.server.advice.GrpcExceptionHandler;
 @GrpcAdvice
 public class ExceptionHandler {
 
+	private static final String ERROR_MESSAGE = "Grpc internal server error";
+
 	static final String KEY_ERROR_CODE = "ERROR_CODE";
 	static final String KEY_EXCEPTION_ID = "EXCEPTION_ID";
 
 	@GrpcExceptionHandler
 	public StatusException handleNotFoundException(NotFoundException e) {
+		LOG.error(ERROR_MESSAGE, e);
 		return createStatusException(buildNotFoundStatus(e), buildMetadata(e));
 	}
 
@@ -55,6 +58,7 @@ public class ExceptionHandler {
 
 	@GrpcExceptionHandler
 	public StatusException handleFunctionalException(FunctionalException e) {
+		LOG.error(ERROR_MESSAGE, e);
 		return createStatusException(buildInternalStatus(e), buildMetadata(e));
 	}
 
@@ -77,6 +81,7 @@ public class ExceptionHandler {
 
 	@GrpcExceptionHandler
 	public StatusException handleTechnicalException(TechnicalException e) {
+		LOG.error(ERROR_MESSAGE, e);
 		return createStatusException(buildInternalStatus(e), buildMetadata(e.getExceptionId()));
 	}
 
@@ -88,7 +93,7 @@ public class ExceptionHandler {
 	public StatusException handleRuntimeException(RuntimeException e) {
 		var exceptionId = createExceptionId();
 		var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId);
-		LOG.error("Grpc internal server error: " + messageWithExceptionId, e);
+		LOG.error("Grpc internal server error: {}", messageWithExceptionId, e);
 
 		return createStatusException(buildInternalStatus(e, messageWithExceptionId), buildMetadata(exceptionId));
 	}
@@ -123,7 +128,7 @@ public class ExceptionHandler {
 	public StatusException handleAccessDeniedException(AccessDeniedException e) {
 		var exceptionId = createExceptionId();
 		var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId);
-		LOG.error("Grpc permission denied error: " + messageWithExceptionId, e);
+		LOG.error("Grpc permission denied error: {}", messageWithExceptionId, e);
 
 		return createStatusException(buildPermissionDeniedStatus(e, messageWithExceptionId), buildMetadata(exceptionId));
 	}
@@ -134,6 +139,7 @@ public class ExceptionHandler {
 
 	@GrpcExceptionHandler
 	public StatusException handleSearchServiceUnavailableException(SearchServiceUnavailableException e) {
+		LOG.error(ERROR_MESSAGE, e);
 		return createStatusException(buildUnavailableStatus(e), buildMetadata(e.getExceptionId()));
 	}
 
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java
new file mode 100644
index 0000000000000000000000000000000000000000..172953ca2820282a18a978e7ac012e7379cd55eb
--- /dev/null
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java
@@ -0,0 +1,68 @@
+/*
+ * 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.vorgang.common.migration;
+
+import org.bson.Document;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.index.CompoundIndexDefinition;
+import org.springframework.data.mongodb.core.index.IndexDefinition;
+
+import io.mongock.api.annotations.ChangeUnit;
+import io.mongock.api.annotations.Execution;
+import io.mongock.api.annotations.RollbackExecution;
+
+@ChangeUnit(id = "2024-06-12 17:00:00 OZG-5661", order = "M010", author = "ebardin")
+public class M010_CreateIndexesInVorgang { // NOSONAR
+
+	static final String COLLECTION_NAME = "vorgang";
+	static final String COMPOUND_INDEX_NAME = "inCreation_OrgaId_assignedTo_status_createdAt";
+
+	static final String FIELD_IN_CREATION = "inCreation";
+	static final String FIELD_STATUS = "status";
+	static final String FIELD_CREATED_AT = "createdAt";
+	static final String FIELD_ASSIGNED_TO = "assignedTo";
+	static final String FIELD_EINGANGS = "eingangs";
+	static final String FIELD_ORGANISATIONSEINHEITEN_ID = FIELD_EINGANGS + ".zustaendigeStelle.organisationseinheitenId";
+
+	@Execution
+	public void doMigration(MongoTemplate template) {
+		template.indexOps(COLLECTION_NAME).ensureIndex(buildCompoundIndexDefinition());
+	}
+
+	IndexDefinition buildCompoundIndexDefinition() {
+		var indexKeys = new Document();
+		indexKeys.put(FIELD_IN_CREATION, 1);
+		indexKeys.put(FIELD_ORGANISATIONSEINHEITEN_ID, 1);
+		indexKeys.put(FIELD_ASSIGNED_TO, 1);
+		indexKeys.put(FIELD_STATUS, 1);
+		indexKeys.put(FIELD_CREATED_AT, 1);
+		return new CompoundIndexDefinition(indexKeys).named(COMPOUND_INDEX_NAME);
+	}
+
+	@RollbackExecution
+	public void rollback() {
+		// kein rollback implementiert
+	}
+
+}
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..105b8bdffbb95a96514c9fe65c0100da5820a12a
--- /dev/null
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.vorgang.common.search;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.index.TextIndexDefinition;
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.vorgang.vorgang.Vorgang;
+import lombok.RequiredArgsConstructor;
+
+@ConditionalOnProperty(prefix = "ozgcloud.mongodbsearch", name = "enabled", havingValue = "true")
+@Component
+@RequiredArgsConstructor
+public class MongodbFullTextSearchIndexInitializer {
+
+	static final String TEXT_INDEX_NAME = "textIndex";
+	static final String TEXT_INDEX_LANGUAGE = "german";
+
+	private final MongoOperations mongoOperations;
+
+	@EventListener(ContextRefreshedEvent.class)
+	public void initWildcardTextIndex() {
+		mongoOperations.indexOps(Vorgang.COLLECTION_NAME).ensureIndex(buildWildcardTextIndexDefinition());
+	}
+
+	TextIndexDefinition buildWildcardTextIndexDefinition() {
+		return new TextIndexDefinition.TextIndexDefinitionBuilder()
+				.named(TEXT_INDEX_NAME)
+				.onAllFields()
+				.withDefaultLanguage(TEXT_INDEX_LANGUAGE)
+				.build();
+	}
+
+}
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java
index 19533cbeee1904c697846e124744baf09a841c31..1d7a92f2e8d7be97c40a63f717454a23c3702942 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java
@@ -23,10 +23,12 @@
  */
 package de.ozgcloud.vorgang.common.search;
 
+import static java.util.Objects.*;
+
 import java.util.Collections;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.dao.DataAccessResourceFailureException;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
@@ -37,10 +39,10 @@ import de.ozgcloud.vorgang.vorgang.Vorgang;
 import de.ozgcloud.vorgang.vorgang.VorgangHeader;
 
 @Service
-@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address")
+@ConditionalOnExpression("!'${ozgcloud.elasticsearch.address:}'.isEmpty() || ${ozgcloud.mongodbsearch.enabled:false}")
 public class SearchService {
 
-	@Autowired
+	@Autowired(required = false)
 	private SearchVorgangRepository repository;
 
 	@Autowired
@@ -49,16 +51,15 @@ public class SearchService {
 	@Autowired
 	private IndexedVorgangMapper mapper;
 
+	// TODO die Methode scheint überfüssig zu sein. Man könnte stattsie und ' updateVorgang' eine Methode z.B. 'addOrUpdateVorgang' nutzen.
 	public void addVorgang(Vorgang vorgang) {
-		repository.save(mapper.fromVorgang(vorgang));
+		updateVorgang(vorgang);
 	}
 
 	public void updateVorgang(Vorgang vorgang) {
-		repository.save(mapper.fromVorgang(vorgang));
-	}
-
-	public boolean existsById(String vorgangId) {
-		return repository.existsById(vorgangId);
+		if (nonNull(repository)) {
+			repository.save(mapper.fromVorgang(vorgang));
+		}
 	}
 
 	public Page<VorgangHeader> find(FindVorgangRequest findVorgangRequest) {
@@ -70,7 +71,9 @@ public class SearchService {
 	}
 
 	public void deleteVorgang(String vorgangId) {
-		repository.deleteById(vorgangId);
+		if (nonNull(repository)) {
+			repository.deleteById(vorgangId);
+		}
 	}
 
 	public void setAktenzeichen(String vorgangId, String aktenzeichen) {
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java
index 7c1a2a19abae7c802e7054c3713d1dfdd6255879..3a58f8f74cfc1c23b8fdc5f217ddc64e488d4244 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java
@@ -25,13 +25,11 @@ package de.ozgcloud.vorgang.common.search;
 
 import java.util.Map;
 
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.data.domain.Page;
 
 import de.ozgcloud.vorgang.vorgang.FindVorgangRequest;
 import de.ozgcloud.vorgang.vorgang.VorgangHeader;
 
-@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address")
 interface SearchVorgangCustomRepository {
 	Page<VorgangHeader> searchBy(FindVorgangRequest request);
 
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java
index cfd983a2828f8edc7d56c2f0457292615becbaca..47fe7b83020c3fbd880df9c0abd3aceaeef23ccd 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java
@@ -39,6 +39,7 @@ import java.util.stream.Stream;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
@@ -61,6 +62,7 @@ import de.ozgcloud.vorgang.vorgang.FindVorgangRequest;
 import de.ozgcloud.vorgang.vorgang.Vorgang;
 import de.ozgcloud.vorgang.vorgang.VorgangHeader;
 
+@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address")
 @Repository
 class SearchVorgangCustomRepositoryImpl implements SearchVorgangCustomRepository {
 
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java
new file mode 100644
index 0000000000000000000000000000000000000000..51570c813a3d09f2ba0373b598384429f736dbaa
--- /dev/null
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java
@@ -0,0 +1,76 @@
+/*
+ * 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.vorgang.common.search;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.TextCriteria;
+import org.springframework.data.mongodb.core.query.TextQuery;
+import org.springframework.data.support.PageableExecutionUtils;
+import org.springframework.stereotype.Repository;
+
+import de.ozgcloud.vorgang.vorgang.FindVorgangRequest;
+import de.ozgcloud.vorgang.vorgang.VorgangHeader;
+import lombok.RequiredArgsConstructor;
+
+@ConditionalOnProperty(prefix = "ozgcloud.mongodbsearch", name = "enabled", havingValue = "true")
+@RequiredArgsConstructor
+@Repository
+public class SearchVorgangCustomRepositoryMongo implements SearchVorgangCustomRepository {
+
+	private final MongoOperations mongoOperations;
+
+	@Override
+	public Page<VorgangHeader> searchBy(final FindVorgangRequest request) {
+		var query = buildQuery(request);
+		var vorgangs = mongoOperations.stream(query, VorgangHeader.class).skip(request.getOffset()).limit(request.getLimit()).toList();
+		return PageableExecutionUtils.getPage(vorgangs, buildPageable(request), vorgangs::size);
+	}
+
+	private Query buildQuery(FindVorgangRequest request) {
+		return TextQuery.queryText(buildTextCriteria(request.getSearchBy())).sortByScore();
+	}
+
+	TextCriteria buildTextCriteria(String searchBy) {
+		var textCriteria = TextCriteria.forDefaultLanguage();
+		Arrays.stream(searchBy.split(StringUtils.SPACE)).forEach(textCriteria::matchingPhrase);
+		return textCriteria;
+	}
+
+	PageRequest buildPageable(FindVorgangRequest request) {
+		return PageRequest.of(request.getOffset() / request.getLimit(), request.getLimit());
+	}
+
+	@Override
+	public void update(final String vorangId, final Map<String, Object> patch) {
+		// update implemented in vorgang.vorgang.VorgangRepository
+	}
+}
diff --git a/vorgang-manager-server/src/main/resources/application-local.yml b/vorgang-manager-server/src/main/resources/application-local.yml
index b1aa3707359cf0cc5a250ea5b76b2a86016d7c06..3c001700cf121f982f772d96606412189ad50db3 100644
--- a/vorgang-manager-server/src/main/resources/application-local.yml
+++ b/vorgang-manager-server/src/main/resources/application-local.yml
@@ -65,9 +65,6 @@ ozgcloud:
   user:
     cleanup:
       cron: 0 */5 * * * *
-  elasticsearch:
-    initEnabled: true
-    index: test-index
   feature:
     bescheid:
       enable-dummy-document-processor: true
@@ -85,10 +82,14 @@ spring:
       - with-elastic
 ozgcloud:
   elasticsearch:
+    initEnabled: true
+    index: test-index
     address: localhost:9200
     username: elastic
     password: password
     useSsl: false
+  mongodbsearch:
+    enabled: false
 
 ---
 
@@ -126,3 +127,14 @@ grpc:
     bayern-id:
       address: static://127.0.0.1:9099
       negotiationType: PLAINTEXT
+      
+---
+
+spring:
+  config:
+    activate:
+      on-profile:
+      - with-mongodbsearch
+ozgcloud:
+  mongodbsearch:
+    enabled: true
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
index 54cfc7b0679835e54404911bcb0c8945533ed8d7..9a57cb6a5f7b082ba783569f1daf7c765b183d5c 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidDocumentITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidDocumentITCase.java
@@ -90,8 +90,8 @@ class BescheidDocumentITCase {
 					.vorgangId("vorgang-id")
 					.relationId("bescheidItem-id")
 					.order(BescheidEventListener.CREATE_BESCHEID_DOCUMENT_ORDER)
-					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
-							GENEHMIGT_BODYKEY, true))
+					.bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01",
+							GENEHMIGT_BODY_KEY, 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/BescheidITCase.java
similarity index 95%
rename from vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
rename to vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidITCase.java
index 17e8ba106578761e7b8965205d94831df337f411..0926bd140fa344c6435b74befbda978e161dbf81 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidITCase.java
@@ -73,8 +73,8 @@ import de.ozgcloud.vorgang.VorgangManagerServerApplication;
 import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem;
 import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory;
 import de.ozgcloud.vorgang.callcontext.CallContextAuthenticationTokenTestFactory;
+import de.ozgcloud.vorgang.callcontext.WithMockCustomUser;
 import de.ozgcloud.vorgang.command.CommandService;
-import de.ozgcloud.vorgang.command.CommandTestFactory;
 import de.ozgcloud.vorgang.command.CreateCommandRequest;
 import de.ozgcloud.vorgang.servicekonto.ServiceKontoTestFactory;
 import de.ozgcloud.vorgang.status.StatusChangedEvent;
@@ -93,7 +93,8 @@ import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
 })
 @DataITCase
 @DirtiesContext
-class BescheidEventListenerITCase {
+@WithMockCustomUser
+class BescheidITCase {
 
 	private static final String USER_ID = "user-id";
 
@@ -133,7 +134,7 @@ class BescheidEventListenerITCase {
 			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
 			var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId();
 
-			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId));
+			commandService.createCommand(buildCreatedCommandRequest(vorgangId, bescheidItemId));
 
 			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
 				verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture());
@@ -146,7 +147,7 @@ class BescheidEventListenerITCase {
 			var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId();
 			var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId();
 
-			eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId));
+			commandService.createCommand(buildCreatedCommandRequest(vorgangId, bescheidItemId));
 
 			await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
 				verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture());
@@ -164,15 +165,14 @@ class BescheidEventListenerITCase {
 					.build();
 		}
 
-		private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, String bescheidItemId) {
-			var command = CommandTestFactory.createBuilder()
+		private CreateCommandRequest buildCreatedCommandRequest(String vorgangId, String bescheidItemId) {
+			return CreateCommandRequest.builder()
 					.vorgangId(vorgangId)
 					.relationId(bescheidItemId)
 					.order(BescheidEventListener.CREATE_BESCHEID_DOCUMENT_ORDER)
-					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
-							GENEHMIGT_BODYKEY, true))
+					.bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01",
+							GENEHMIGT_BODY_KEY, true))
 					.build();
-			return new CommandCreatedEvent(command);
 		}
 
 		private GridFSFile getBescheidDocumentFile() {
@@ -236,15 +236,15 @@ class BescheidEventListenerITCase {
 		}
 
 		private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, VorgangAttachedItem bescheidItem) {
-			var command = CommandTestFactory.createBuilder()
+			var createCommandRequest = CreateCommandRequest.builder()
 					.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))
+					.bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01",
+							GENEHMIGT_BODY_KEY, true))
 					.build();
-			return new CommandCreatedEvent(command);
+			return new CommandCreatedEvent(commandService.createCommand(createCommandRequest));
 		}
 
 		private VorgangAttachedItem loadBescheid(String bescheidId) {
@@ -405,8 +405,8 @@ class BescheidEventListenerITCase {
 					.relationId(bescheidItem.getId())
 					.relationVersion(bescheidItem.getVersion())
 					.order(SEND_BESCHEID_ORDER)
-					.bodyObject(Map.of(BESCHEID_VOM_BODYKEY, "2024-01-01",
-							GENEHMIGT_BODYKEY, true))
+					.bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01",
+							GENEHMIGT_BODY_KEY, true))
 					.build();
 		}
 
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ab89f43444d623ec62d3cd493d4b2155e06c276
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.vorgang;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Optional;
+
+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.user.OzgCloudUserId;
+import de.ozgcloud.vorgang.callcontext.CallContextUser;
+import de.ozgcloud.vorgang.callcontext.CurrentUserService;
+import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor;
+
+class VorgangManagerCallContextProviderTest {
+
+	private static final String USER_ID = "userId";
+
+	@Spy
+	@InjectMocks
+	private VorgangManagerCallContextProvider contextProvider;
+
+	@Mock
+	private CurrentUserService currentUserService;
+
+	@Nested
+	class TestProvideContext {
+
+		@Test
+		void shouldSetClientName() {
+			var result = contextProvider.provideContext();
+
+			assertThat(result.getClientName()).isEqualTo(VorgangManagerClientCallContextAttachingInterceptor.VORGANG_MANAGER_CLIENT_NAME);
+		}
+
+		@Test
+		void shouldCallFindUserId() {
+			contextProvider.provideContext();
+
+			verify(contextProvider).findUserId();
+		}
+
+		@Test
+		void shouldSetUserId() {
+			var userId = OzgCloudUserId.from(USER_ID);
+			when(contextProvider.findUserId()).thenReturn(Optional.of(userId));
+
+			var result = contextProvider.provideContext();
+
+			assertThat(result.getUserId()).isEqualTo(userId);
+		}
+	}
+
+	@Nested
+	class TestFindUserId {
+
+		@Test
+		void shouldCallCurrentUserService() {
+			contextProvider.findUserId();
+
+			verify(currentUserService).findUser();
+		}
+
+		@Test
+		void shouldReturnUserId() {
+			var callContextUser = CallContextUser.builder().userId(Optional.of(USER_ID)).build();
+			when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser));
+
+			var result = contextProvider.findUserId();
+
+			assertThat(result).contains(OzgCloudUserId.from(USER_ID));
+		}
+
+		@Test
+		void shouldReturnIfNoUser() {
+			var result = contextProvider.findUserId();
+
+			assertThat(result).isEmpty();
+		}
+
+		@Test
+		void shouldReturnIfNoUserId() {
+			var callContextUser = CallContextUser.builder().userId(Optional.empty()).build();
+			when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser));
+
+			var result = contextProvider.findUserId();
+
+			assertThat(result).isEmpty();
+		}
+	}
+}
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java
index 79ce07e580ab83de2d02838ad6837fc009116650..2612de99af4c87b621fdbd554082ffe1f0fd6ae8 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java
@@ -45,13 +45,13 @@ import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.security.access.AccessDeniedException;
 
-import de.ozgcloud.command.Command;
 import de.ozgcloud.command.CommandCreatedEvent;
 import de.ozgcloud.common.test.DataITCase;
 import de.ozgcloud.vorgang.callcontext.CallContextUserTestFactory;
 import de.ozgcloud.vorgang.callcontext.CurrentUserService;
-import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory;
-import de.ozgcloud.vorgang.command.CommandTestFactory;
+import de.ozgcloud.vorgang.command.CommandService;
+import de.ozgcloud.vorgang.command.CreateCommandRequest;
+import de.ozgcloud.vorgang.command.CreateCommandRequestTestFactory;
 import de.ozgcloud.vorgang.command.Order;
 import de.ozgcloud.vorgang.vorgang.Vorgang;
 import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
@@ -73,6 +73,8 @@ class VorgangAttachedItemITCase {
 	private ApplicationEventPublisher publisher;
 	@Autowired
 	private MongoOperations mongoOperations;
+	@Autowired
+	private CommandService commandService;
 
 	@BeforeEach
 	void prepareDatabase() {
@@ -83,13 +85,16 @@ class VorgangAttachedItemITCase {
 	@Nested
 	class TestCreateItem {
 
-		private Command command = CommandTestFactory.createBuilder().order(Order.CREATE_ATTACHED_ITEM.toString())
-				.relationId(VorgangTestFactory.ID).bodyObject(VorgangAttachedItemTestFactory.asMap()).build();
-		private CommandCreatedEvent event = CommandCreatedEventTestFactory.create(command);
+		private final CreateCommandRequest createCommandRequest = CreateCommandRequestTestFactory.createBuilder()
+				.order(Order.CREATE_ATTACHED_ITEM.name())
+				.relationId(VorgangTestFactory.ID)
+				.bodyObject(VorgangAttachedItemTestFactory.asMap())
+				.build();
 
 		@Test
 		void shouldPersistInDatabase() {
-			publisher.publishEvent(event);
+			commandService.createCommand(createCommandRequest);
+
 			await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
 				var loadedItems = mongoOperations.findAll(VorgangAttachedItem.class);
 
@@ -106,7 +111,7 @@ class VorgangAttachedItemITCase {
 	@Nested
 	class TestUpdateItem {
 
-		private Command command;
+		private CreateCommandRequest createCommandRequest;
 		private CommandCreatedEvent event;
 
 		private VorgangAttachedItem persistedItem;
@@ -123,18 +128,16 @@ class VorgangAttachedItemITCase {
 
 			var bodyObjectMap = new HashMap<>(VorgangAttachedItemTestFactory.asMap());
 			bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_ITEM, itemMap);
-			command = CommandTestFactory.createBuilder()
+			createCommandRequest = CreateCommandRequestTestFactory.createBuilder()
 					.order(Order.UPDATE_ATTACHED_ITEM.name())
 					.relationId(persistedItem.getId())
 					.relationVersion(persistedItem.getVersion())
 					.bodyObject(bodyObjectMap).build();
-
-			event = CommandCreatedEventTestFactory.create(command);
 		}
 
 		@Test
 		void shouldIncrementVersion() {
-			publisher.publishEvent(event);
+			commandService.createCommand(createCommandRequest);
 
 			await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
 				var loaded = mongoOperations.findAll(VorgangAttachedItem.class);
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java
index 8145e0f6cd15c9f4c51d9323b9e2f2fae33ff819..f9c325212a072878c07fa8e88769812d229fa510 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java
@@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.*;
 
 import java.util.Collections;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -43,6 +44,8 @@ import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
 @DataITCase
 class VorgangAttachedItemRepositoryITCase {
 
+	private static final Map<String, Object> UPDATE_ITEM_MAP = Map.of("dontTouch", 600);
+
 	@Autowired
 	private VorgangAttachedItemRepository repository;
 	@Autowired
@@ -145,19 +148,90 @@ class VorgangAttachedItemRepositoryITCase {
 		}
 	}
 
+	@Nested
+	class TestUpdate {
+
+		@BeforeEach
+		void init() {
+			persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build());
+		}
+
+		@Test
+		void shouldUpdate() {
+			var result = updateItem();
+
+			assertThat(result.getItem()).containsExactlyEntriesOf(UPDATE_ITEM_MAP);
+			assertThat(result.getVersion()).isEqualTo(2);
+		}
+
+		@Test
+		void shouldNotUpdateDeletedItem() {
+			persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build());
+
+			var result = updateItem();
+
+			assertThat(result.getItem()).isEqualTo(VorgangAttachedItemTestFactory.STRING_MAP);
+			assertThat(result.getVersion()).isEqualTo(1);
+		}
+
+		@Test
+		void shouldFailOnCollision() {
+			var itemId = persistedItem.getId();
+
+			assertThrows(ConcurrentModificationException.class, () -> repository.update(itemId, 0, UPDATE_ITEM_MAP));
+		}
+
+		VorgangAttachedItem updateItem() {
+			repository.update(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP);
+			return mongoOperations.findById(persistedItem.getId(), VorgangAttachedItem.class);
+		}
+
+	}
+
+	@Nested
+	class TestForceUpdate {
+
+		@BeforeEach
+		void init() {
+			persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build());
+		}
+
+		@Test
+		void shouldUpdate() {
+			var result = forceUpdateItem();
+
+			assertThat(result.getItem()).containsExactlyEntriesOf(UPDATE_ITEM_MAP);
+			assertThat(result.getVersion()).isEqualTo(2);
+		}
+
+		@Test
+		void shouldNotUpdateDeletedItem() {
+			persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build());
+
+			var result = forceUpdateItem();
+
+			assertThat(result.getItem()).isEqualTo(VorgangAttachedItemTestFactory.STRING_MAP);
+			assertThat(result.getVersion()).isEqualTo(1);
+		}
+
+		VorgangAttachedItem forceUpdateItem() {
+			repository.forceUpdate(persistedItem.getId(), UPDATE_ITEM_MAP);
+			return mongoOperations.findById(persistedItem.getId(), VorgangAttachedItem.class);
+		}
+
+	}
+
 	@Nested
 	class TestPatch {
 
 		@BeforeEach
 		void prepareDatabase() {
-			mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME);
-
 			persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build());
 		}
 
 		@Test
 		void shouldUpdateFieldValue() {
-			repository.patch(persistedItem.getId(), persistedItem.getVersion(), buildPatchMap());
+			repository.patch(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP);
 
 			var loaded = findVorgangAttachedItem();
 			assertThat(loaded).isNotNull();
@@ -178,7 +252,7 @@ class VorgangAttachedItemRepositoryITCase {
 
 		@Test
 		void shouldIncreaseVersion() {
-			repository.patch(persistedItem.getId(), persistedItem.getVersion(), buildPatchMap());
+			repository.patch(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP);
 
 			var loaded = findVorgangAttachedItem();
 			assertThat(loaded).isNotNull();
@@ -187,7 +261,7 @@ class VorgangAttachedItemRepositoryITCase {
 
 		@Test
 		void shouldNotUpdateOnWrongVersion() {
-			repository.patch(persistedItem.getId(), 42, buildPatchMap());
+			repository.patch(persistedItem.getId(), 42, UPDATE_ITEM_MAP);
 
 			var loaded = findVorgangAttachedItem();
 
@@ -198,33 +272,40 @@ class VorgangAttachedItemRepositoryITCase {
 		void shouldNotPatchDeletedItem() {
 			var deletedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build());
 
-			repository.patch(deletedItem.getId(), deletedItem.getVersion(), buildPatchMap());
+			repository.patch(deletedItem.getId(), deletedItem.getVersion(), UPDATE_ITEM_MAP);
 
 			var loaded = mongoOperations.findById(deletedItem.getId(), VorgangAttachedItem.class);
 			assertThat(loaded).usingRecursiveComparison().isEqualTo(deletedItem);
 		}
 
-		private Map<String, Object> buildPatchMap() {
-			return Map.of("dontTouch", 600);
-		}
 	}
 
 	@Nested
-	class TestForceUpdate {
+	class TestForcePatch {
+
+		@Test
+		void shouldPatch() {
+			var expectedItemMap = new HashMap<>(VorgangAttachedItemTestFactory.STRING_MAP);
+			expectedItemMap.putAll(UPDATE_ITEM_MAP);
+			var attachedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build());
+
+			repository.forcePatch(attachedItem.getId(), UPDATE_ITEM_MAP);
+
+			var result = mongoOperations.findById(attachedItem.getId(), VorgangAttachedItem.class);
+			assertThat(result.getItem()).containsExactlyEntriesOf(expectedItemMap);
+			assertThat(result.getVersion()).isEqualTo(2);
+		}
 
 		@Test
 		void shouldNotPatchDeletedItem() {
 			var deletedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build());
 
-			repository.forcePatch(deletedItem.getId(), buildPatchMap());
+			repository.forcePatch(deletedItem.getId(), UPDATE_ITEM_MAP);
 
 			var loaded = mongoOperations.findById(deletedItem.getId(), VorgangAttachedItem.class);
 			assertThat(loaded).usingRecursiveComparison().isEqualTo(deletedItem);
 		}
 
-		private Map<String, Object> buildPatchMap() {
-			return Map.of("dontTouch", 600);
-		}
 	}
 
 	@Nested
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java
index 126d872284154d990daa7e429e7d866ca4988fae..e7d01613b0258fb7da57ad9b862b463e4f6ebaf7 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java
@@ -36,6 +36,8 @@ import java.util.UUID;
 import org.junit.jupiter.api.DisplayName;
 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;
@@ -114,36 +116,43 @@ class VorgangAttachedItemServiceTest {
 	@Nested
 	class TestUpdateItem {
 
+		@Captor
+		private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor;
 		@Test
 		void shouldCallRepository() {
 			service.update(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create());
 
-			verify(repository).patch(any(), anyLong(), any());
+			verify(repository).update(VorgangAttachedItemTestFactory.ID, VERSION, STRING_MAP);
 		}
 
 		@Test
 		void shouldPublishEvent() {
 			service.update(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create());
 
-			verify(publisher).publishEvent(any(VorgangAttachedItemUpdatedEvent.class));
+			verify(publisher).publishEvent(eventCaptor.capture());
+			assertThat(eventCaptor.getValue()).extracting(VorgangAttachedItemUpdatedEvent::getSource).isEqualTo(CommandTestFactory.ID);
 		}
 	}
 
 	@Nested
 	class TestForceUpdateItem {
 
+		@Captor
+		private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor;
+
 		@Test
 		void shouldCallRepository() {
 			service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create());
 
-			verify(repository).forcePatch(eq(VorgangAttachedItemTestFactory.ID), any());
+			verify(repository).forceUpdate(VorgangAttachedItemTestFactory.ID, STRING_MAP);
 		}
 
 		@Test
 		void shouldPublishEvent() {
 			service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create());
 
-			verify(publisher).publishEvent(any(VorgangAttachedItemUpdatedEvent.class));
+			verify(publisher).publishEvent(eventCaptor.capture());
+			assertThat(eventCaptor.getValue()).extracting(VorgangAttachedItemUpdatedEvent::getSource).isEqualTo(CommandTestFactory.ID);
 		}
 	}
 
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java
index 153af77891fc1df9973ceb7aee9b90251fd32bc7..fe0a42a246cc4018afe82709c0da52cb3a6109cd 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java
@@ -95,4 +95,5 @@ class CommandEventListenerTest {
 			verify(commandService).setCommandRevoked(CommandTestFactory.ID);
 		}
 	}
+
 }
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java
index cebb7e89bf49c7a6b6e4f4e71df53c4ec6f22020..ffb8365e0580e683c02e836748516f4f0d9a5214 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java
@@ -599,4 +599,50 @@ class CommandRepositoryITCase {
 			assertThat(result).isEmpty();
 		}
 	}
+
+	@Nested
+	class TestSetRevokeStatus {
+
+		@Test
+		void shouldSetCanceled() {
+			var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.NEW).build());
+
+			repository.setRevokeStatus(command.getId());
+
+			var result = repository.getById(command.getId());
+			assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.CANCELED);
+		}
+
+		@Test
+		void shouldSetRevokePendingWhenPending() {
+			var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).build());
+
+			repository.setRevokeStatus(command.getId());
+
+			var result = repository.getById(command.getId());
+			assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.REVOKE_PENDING);
+		}
+
+		@Test
+		void shouldSetRevokePendingWhenFinished() {
+			var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.FINISHED).build());
+
+			repository.setRevokeStatus(command.getId());
+
+			var result = repository.getById(command.getId());
+			assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.REVOKE_PENDING);
+		}
+
+		@DisplayName("should not update when")
+		@ParameterizedTest(name = "status is {0}")
+		@EnumSource(value = CommandStatus.class, names = { "NEW", "PENDING", "FINISHED" }, mode = EnumSource.Mode.EXCLUDE)
+		void shouldNotUpdate(CommandStatus status) {
+			var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(status).build());
+
+			repository.setRevokeStatus(command.getId());
+
+			var result = repository.getById(command.getId());
+			assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(status);
+		}
+	}
 }
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java
index b34413c437df90ab5766b7421d7a527dd81c8319..7e5383aa33fd5870f925b8e33f9a0b194e15b191 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java
@@ -203,73 +203,124 @@ class CommandServiceTest {
 	class TestSetCommandFinished {
 
 		@Test
-		void shouldCallFinishCommand() {
-			service.setCommandFinished(CommandTestFactory.ID);
+		void shouldThrowExceptionIfCommandNotFound() {
+			doThrow(NotFoundException.class).when(service).getCommand(anyString());
 
-			verify(service).setCommandFinished(CommandTestFactory.ID, null);
+			assertThrows(NotFoundException.class, () -> service.setCommandFinished(CommandTestFactory.ID));
 		}
 
-		@Test
-		void shouldCallRepositoryFinishCommand() {
-			service.setCommandFinished(CommandTestFactory.ID, null);
+		@Nested
+		class TestRevokeCommand {
 
-			verify(repository).finishCommand(CommandTestFactory.ID);
-		}
+			private Command command;
 
-		@Test
-		void shouldCallRepositoryFinishCommandWithCreatedResource() {
-			service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+			@BeforeEach
+			void init() {
+				command = CommandTestFactory.createBuilder().status(CommandStatus.REVOKE_PENDING).build();
+				doReturn(command).when(service).getCommand(anyString());
+			}
 
-			verify(repository).finishCommand(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
-		}
+			@Test
+			void shouldCallPublishRevokeCommandEvent() {
+				service.setCommandFinished(CommandTestFactory.ID);
 
-		@Test
-		void shouldCallGetCompletableParentId() {
-			service.setCommandFinished(CommandTestFactory.ID);
+				verify(service).publishRevokeCommandEvent(command);
+			}
+
+			@Test
+			void shouldNotCallFinishCommandIfRevokePending() {
+				service.setCommandFinished(CommandTestFactory.ID);
+
+				verify(repository, never()).finishCommand(anyString());
+				verify(repository, never()).finishCommand(anyString(), anyString());
+			}
 
-			verify(service).getCompletableParentId(CommandTestFactory.ID);
 		}
 
-		@Test
-		void shouldCallExistsNotFinishedSubCommands() {
-			var parentId = "parent-id";
-			doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
+		@Nested
+		class TestFinishCommand {
 
-			service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+			@BeforeEach
+			void init() {
+				doReturn(CommandTestFactory.create()).when(service).getCommand(anyString());
+			}
 
-			verify(repository).existsNotFinishedSubCommands(parentId);
-		}
+			@Test
+			void shouldCallGetCommand() {
+				service.setCommandFinished(CommandTestFactory.ID);
 
-		@Test
-		void shouldCallPublishCommandExecutedEvent() {
-			var parentId = "parent-id";
-			doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
+				verify(service).getCommand(CommandTestFactory.ID);
+			}
 
-			service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+			@Test
+			void shouldCallFinishCommand() {
+				service.setCommandFinished(CommandTestFactory.ID);
 
-			verify(service).publishCommandExecutedEvent(parentId);
-		}
+				verify(service).setCommandFinished(CommandTestFactory.ID, null);
+			}
 
-		@DisplayName("should not call publishCommandExecutedEvent when no parent id")
-		@Test
-		void shouldNotCallPublishCommandExecutedEvent() {
-			doReturn(Optional.empty()).when(service).getCompletableParentId(any());
+			@Test
+			void shouldCallRepositoryFinishCommand() {
+				service.setCommandFinished(CommandTestFactory.ID, null);
 
-			service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+				verify(repository).finishCommand(CommandTestFactory.ID);
+			}
 
-			verify(service, never()).publishCommandExecutedEvent(anyString());
-		}
+			@Test
+			void shouldCallRepositoryFinishCommandWithCreatedResource() {
+				service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
 
-		@DisplayName("should not call publishCommandExecutedEvent when not all subcommands are finished")
-		@Test
-		void shouldNotCallPublishCommandExecutedEvent1() {
-			var parentId = "parent-id";
-			doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
-			when(repository.existsNotFinishedSubCommands(any())).thenReturn(true);
+				verify(repository).finishCommand(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+			}
+
+			@Test
+			void shouldCallGetCompletableParentId() {
+				service.setCommandFinished(CommandTestFactory.ID);
+
+				verify(service).getCompletableParentId(CommandTestFactory.ID);
+			}
+
+			@Test
+			void shouldCallExistsNotFinishedSubCommands() {
+				var parentId = "parent-id";
+				doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
+
+				service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+
+				verify(repository).existsNotFinishedSubCommands(parentId);
+			}
+
+			@Test
+			void shouldCallPublishCommandExecutedEvent() {
+				var parentId = "parent-id";
+				doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
+
+				service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+
+				verify(service).publishCommandExecutedEvent(parentId);
+			}
+
+			@DisplayName("should not call publishCommandExecutedEvent when no parent id")
+			@Test
+			void shouldNotCallPublishCommandExecutedEvent() {
+				doReturn(Optional.empty()).when(service).getCompletableParentId(any());
+
+				service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+
+				verify(service, never()).publishCommandExecutedEvent(anyString());
+			}
+
+			@DisplayName("should not call publishCommandExecutedEvent when not all subcommands are finished")
+			@Test
+			void shouldNotCallPublishCommandExecutedEvent1() {
+				var parentId = "parent-id";
+				doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any());
+				when(repository.existsNotFinishedSubCommands(any())).thenReturn(true);
 
-			service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
+				service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE);
 
-			verify(service, never()).publishCommandExecutedEvent(anyString());
+				verify(service, never()).publishCommandExecutedEvent(anyString());
+			}
 		}
 	}
 
@@ -471,62 +522,60 @@ class CommandServiceTest {
 	}
 
 	@Nested
-	class TestRevokeCommandPending {
+	class TestRevokeCommand {
 
 		final String commandId = CommandTestFactory.ID;
 
 		@Test
 		void shouldThrowException() {
-			assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.setCommandRevokePending(commandId));
+			assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> service.revokeCommand(commandId));
 		}
 
 		@Test
-		void shouldUpdateCommandStatus() {
-			when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create()));
+		void shouldCallSetRevokeStatus() {
+			doReturn(CommandTestFactory.create()).when(repository).setRevokeStatus(anyString());
 
-			service.setCommandRevokePending(commandId);
+			service.revokeCommand(commandId);
 
-			verify(repository).updateCommandStatus(anyString(), any(CommandStatus.class));
+			verify(service).setRevokeStatus(CommandTestFactory.ID);
 		}
 
 		@Test
 		void shouldCallPublishRevokeCommandEvent() {
-			when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create()));
+			var command = CommandTestFactory.create();
+			doReturn(command).when(repository).setRevokeStatus(anyString());
 
-			service.setCommandRevokePending(commandId);
+			service.revokeCommand(commandId);
 
-			verify(service).publishRevokeCommandEvent(any(String.class));
+			verify(service).publishRevokeCommandEvent(command);
 		}
-	}
-
-	@Nested
-	class TestFiringRevokeCommand {
-
-		final String commandId = CommandTestFactory.ID;
 
 		@Test
-		void shoudlFireEvent() {
-			when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create()));
+		void shouldReturnCommand() {
+			var command = CommandTestFactory.create();
+			doReturn(command).when(repository).setRevokeStatus(anyString());
 
-			service.publishRevokeCommandEvent(commandId);
+			var result = service.revokeCommand(commandId);
 
-			verify(publisher).publishEvent(any(RevokeCommandEvent.class));
+			assertThat(result).isSameAs(command);
 		}
+	}
 
-		@Test
-		void shoudlThrowTechnicalExceptionBecauseCommandNotFound() {
-			when(repository.getById(commandId)).thenReturn(Optional.empty());
+	@Nested
+	class TestPublishRevokeCommandEvent {
 
-			assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.publishRevokeCommandEvent(commandId));
-		}
+		@Captor
+		private ArgumentCaptor<RevokeCommandEvent> eventCaptor;
 
 		@Test
-		void shoudlThrowTechnicalExceptionBecauseNoPreviousState() {
-			when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.createBuilder().previousState(null).build()));
+		void shouldPublishEvent() {
+			var command = CommandTestFactory.create();
 
-			assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.publishRevokeCommandEvent(commandId));
-		}
+			service.publishRevokeCommandEvent(command);
 
+			verify(publisher).publishEvent(eventCaptor.capture());
+			assertThat(eventCaptor.getValue().getSource()).isSameAs(command);
+		}
 	}
 
 	@Nested
@@ -807,4 +856,25 @@ class CommandServiceTest {
 			assertThat(result).containsAllEntriesOf(BODY_OBJ);
 		}
 	}
+
+	@Nested
+	class TestSetStatusRevokePending {
+
+		@Test
+		void shouldCallRepository() {
+			service.setRevokeStatus(CommandTestFactory.ID);
+
+			verify(repository).setRevokeStatus(CommandTestFactory.ID);
+		}
+
+		@Test
+		void shouldReturnUpdatedCommand() {
+			var expectedCommand = CommandTestFactory.create();
+			when(repository.setRevokeStatus(anyString())).thenReturn(expectedCommand);
+
+			var result = service.setRevokeStatus(CommandTestFactory.ID);
+
+			assertThat(result).isSameAs(expectedCommand);
+		}
+	}
 }
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java
index 3343f674fda73345c563128d368532ee235f60d1..3053fad919a61cbdc56dfc0a5e0a46fd92a193e8 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java
@@ -39,6 +39,7 @@ import de.ozgcloud.vorgang.grpc.command.GrpcCreateCommand;
 import de.ozgcloud.vorgang.grpc.command.GrpcCreateCommandRequest;
 import de.ozgcloud.vorgang.grpc.command.GrpcFindCommandsRequest;
 import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest;
+import de.ozgcloud.vorgang.grpc.command.GrpcRevokeCommandRequest;
 import de.ozgcloud.vorgang.vorgang.Vorgang;
 import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
 import io.grpc.StatusRuntimeException;
@@ -111,6 +112,29 @@ class GrpcCommandServiceITCase {
 		}
 	}
 
+	@Nested
+	class TestRevokeCommand {
+
+		@Test
+		void shouldHaveSetStatus() {
+			var vorgang = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build());
+			var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.FINISHED).vorgangId(vorgang.getId())
+					.previousState(Map.of(Vorgang.MONGODB_FIELDNAME_STATUS, "NEW")).build());
+
+			var result = serviceBlockingStub.revokeCommand(GrpcRevokeCommandRequest.newBuilder().setId(command.getId()).build());
+
+			assertThat(result.getCommand().getStatus()).isEqualTo(CommandStatus.REVOKE_PENDING.name());
+		}
+
+		@Test
+		void shouldThrowExceptionIfCommandNotFound() {
+			var revokeRequest = GrpcRevokeCommandRequest.newBuilder().setId("not-existing-id").build();
+
+			assertThrows(StatusRuntimeException.class, () -> serviceBlockingStub.revokeCommand(revokeRequest));
+		}
+
+	}
+
 	@Nested
 	class TestLoadOutdatedOrders {
 
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java
index 3924b31aeb817ab048897bf4de28ddec744b416e..dcc534d8db4fbf2dceb44d5ebcdaae1865c4db12 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java
@@ -36,9 +36,6 @@ 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.junit.jupiter.params.provider.EnumSource.Mode;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.InjectMocks;
@@ -66,7 +63,6 @@ import de.ozgcloud.vorgang.grpc.command.GrpcRevokeCommandRequest;
 import de.ozgcloud.vorgang.grpc.command.GrpcSetCommandExecutedRequest;
 import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
 import io.grpc.stub.StreamObserver;
-import lombok.SneakyThrows;
 
 class GrpcCommandServiceTest {
 
@@ -147,29 +143,6 @@ class GrpcCommandServiceTest {
 		}
 	}
 
-	@Nested
-	class TestIsStatusChangeOrder {
-
-		@ParameterizedTest
-		@EnumSource(mode = Mode.INCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN",
-				"VORGANG_BESCHEIDEN", "VORGANG_ZURUECKSTELLEN", "VORGANG_ABSCHLIESSEN", "VORGANG_WIEDEREROEFFNEN" })
-		void shouldReturnTrue(Order order) {
-			var result = service.isStatusChangeOrder(order.name());
-
-			assertThat(result).isTrue();
-		}
-
-		@ParameterizedTest
-		@EnumSource(mode = Mode.EXCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN",
-				"VORGANG_BESCHEIDEN", "VORGANG_ZURUECKSTELLEN", "VORGANG_ABSCHLIESSEN", "VORGANG_WIEDEREROEFFNEN", "VORGANG_ZUM_LOESCHEN_MARKIEREN",
-				"VORGANG_LOESCHEN" })
-		void shouldReturnFalse(Order order) {
-			var result = service.isStatusChangeOrder(order.name());
-
-			assertThat(result).isFalse();
-		}
-	}
-
 	@DisplayName("Get command")
 	@Nested
 	class TestGetCommand {
@@ -228,56 +201,55 @@ class GrpcCommandServiceTest {
 
 		@Mock
 		private StreamObserver<GrpcCommandResponse> responseObserver;
-		private final GrpcRevokeCommandRequest request = GrpcRevokeCommandRequest.newBuilder().setId(CommandTestFactory.ID).build();
-
-		@BeforeEach
-		void mockCommandService() {
-			Command vorgangCommand = CommandTestFactory.createBuilder().id(CommandTestFactory.ID).build();
+		private final Command updatedCommand = CommandTestFactory.createBuilder().id(CommandTestFactory.ID).build();
+		@Mock
+		private GrpcCommandResponse commandsResponse;
 
-			when(commandService.findCommand(any())).thenReturn(Optional.of(vorgangCommand));
-		}
+		private final GrpcRevokeCommandRequest request = GrpcRevokeCommandRequest.newBuilder().setId(CommandTestFactory.ID).build();
+		@Captor
+		private ArgumentCaptor<CommandResponse> responseCaptor;
 
 		@Test
-		void shouldCallPolicyService() throws Exception {
+		void shouldCallPolicyService() {
 			callRevokeCommand();
 
-			verify(policyService).checkPermissionByCommand(anyString());
+			verify(policyService).checkPermissionByCommand(CommandTestFactory.ID);
 		}
 
 		@Test
-		void shouldCallServiceRevokeCommand() throws Exception {
+		void shouldCallServiceRevokeCommand() {
 			callRevokeCommand();
 
-			verify(commandService).setCommandRevokePending(request.getId());
+			verify(commandService).revokeCommand(CommandTestFactory.ID);
 		}
 
 		@Test
-		void shouldCallMapper() throws Exception {
+		void shouldCallMapper() {
+			when(commandService.revokeCommand(any())).thenReturn(updatedCommand);
+
 			callRevokeCommand();
 
-			verify(commandResponseMapper).toGrpc(any());
+			verify(commandResponseMapper).toGrpc(responseCaptor.capture());
+			assertThat(responseCaptor.getValue()).extracting(CommandResponse::getCommand).isSameAs(updatedCommand);
 		}
 
-		@Nested
-		class TestProceedRevokeCommand {
+		@Test
+		void shouldCallOnNext() {
+			when(commandResponseMapper.toGrpc(any())).thenReturn(commandsResponse);
 
-			private final PersistedCommand command = CommandTestFactory.create();
+			callRevokeCommand();
 
-			@BeforeEach
-			void init() {
-				when(commandService.findCommand(anyString())).thenReturn(Optional.of(command));
-			}
+			verify(responseObserver).onNext(commandsResponse);
+		}
 
-			@Test
-			@SneakyThrows
-			void shouldLoadCommand() {
-				callRevokeCommand();
+		@Test
+		void shouldCloseStream() {
+			callRevokeCommand();
 
-				verify(service).getCommand(CommandTestFactory.ID);
-			}
+			verify(responseObserver).onCompleted();
 		}
 
-		private void callRevokeCommand() throws Exception {
+		private void callRevokeCommand() {
 			service.revokeCommand(request, responseObserver);
 		}
 	}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2751ccceaf4480dec515641851e051de8eba4f1
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.vorgang.common.migration;
+
+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 org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.index.IndexDefinition;
+import org.springframework.data.mongodb.core.index.IndexOperations;
+
+class M010_CreateIndexesInVorgangTest {
+
+	@Spy
+	@InjectMocks
+	private M010_CreateIndexesInVorgang migration;
+
+	@Mock
+	private MongoTemplate template;
+	@Mock
+	private IndexOperations indexOperations;
+
+	@Nested
+	class TestDoMigration {
+
+		@Mock
+		private IndexDefinition indexDefinition;
+
+		@BeforeEach
+		void init() {
+			when(template.indexOps(anyString())).thenReturn(indexOperations);
+		}
+
+		@Test
+		void shouldGetIndexOperations() {
+			migration.doMigration(template);
+
+			verify(template).indexOps(M010_CreateIndexesInVorgang.COLLECTION_NAME);
+		}
+
+		@Test
+		void shouldCallBuildCompoundIndexDefinition() {
+			migration.doMigration(template);
+
+			verify(migration).buildCompoundIndexDefinition();
+		}
+
+		@Test
+		void shouldEnsureComponentIndex() {
+			doReturn(indexDefinition).when(migration).buildCompoundIndexDefinition();
+
+			migration.doMigration(template);
+
+			verify(indexOperations).ensureIndex(indexDefinition);
+		}
+
+	}
+
+	@Nested
+	class TestBuildCompoundIndex {
+
+		@Test
+		void shouldSetInCreation() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_IN_CREATION, 1);
+		}
+
+		@Test
+		void shouldSetOrganisationseinheitenId() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_ORGANISATIONSEINHEITEN_ID, 1);
+		}
+
+		@Test
+		void shouldSetAssignedTo() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_ASSIGNED_TO, 1);
+		}
+
+		@Test
+		void shouldSetStatus() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_STATUS, 1);
+		}
+
+		@Test
+		void shouldSetCreatedAt() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_CREATED_AT, 1);
+		}
+
+		@Test
+		void shouldSetName() {
+			var indexDefinition = migration.buildCompoundIndexDefinition();
+
+			assertThat(indexDefinition.getIndexOptions()).containsEntry("name", M010_CreateIndexesInVorgang.COMPOUND_INDEX_NAME);
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..c623040a7e824441be13e2bcb82676e0f19e2ec2
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java
@@ -0,0 +1,63 @@
+/*
+ * 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.vorgang.common.search;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.awaitility.Awaitility.*;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.index.IndexInfo;
+
+import de.ozgcloud.common.test.DataITCase;
+import de.ozgcloud.vorgang.vorgang.Vorgang;
+
+@SpringBootTest(properties = {
+		"ozgcloud.mongodbsearch.enabled=true"
+})
+@DataITCase
+class MongodbFullTextSearchIndexInitializerITCase {
+
+	@Autowired
+	private MongoOperations mongoOperations;
+
+	@Test
+	void shouldEnsureIndex() {
+		await().atMost(Duration.ofSeconds(60)).untilAsserted(() ->
+				assertThat(mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo()).isNotEmpty());
+
+		assertThat(getTextIndex()).isPresent();
+	}
+
+	private Optional<IndexInfo> getTextIndex() {
+		return mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo().stream()
+				.filter(index -> MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME.equals(index.getName())).findFirst();
+	}
+
+}
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e93887973173fa3a6e986891adfe6c5fd28e9b9
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.vorgang.common.search;
+
+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 org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.index.IndexOperations;
+import org.springframework.data.mongodb.core.index.TextIndexDefinition;
+
+import de.ozgcloud.vorgang.vorgang.Vorgang;
+
+class MongodbFullTextSearchIndexInitializerTest {
+
+	@Spy
+	@InjectMocks
+	private MongodbFullTextSearchIndexInitializer indexInitializer;
+
+	@Mock
+	private MongoOperations mongoOperations;
+
+	@Nested
+	class TestInitWildcardTextIndex {
+
+		@Mock
+		private IndexOperations indexOperations;
+		@Mock
+		private TextIndexDefinition textIndexDefinition;
+
+		@BeforeEach
+		void init() {
+			when(mongoOperations.indexOps(anyString())).thenReturn(indexOperations);
+		}
+
+		@Test
+		void shouldCallEnsureIndex() {
+			indexInitializer.initWildcardTextIndex();
+
+			verify(mongoOperations).indexOps(Vorgang.COLLECTION_NAME);
+		}
+
+		@Test
+		void shouldCallBuildWildcardTextIndexDefinition() {
+			indexInitializer.initWildcardTextIndex();
+
+			verify(indexInitializer).buildWildcardTextIndexDefinition();
+		}
+
+		@Test
+		void shouldCallEnsureIndexWithBuildWildcardTextIndexDefinition() {
+			doReturn(textIndexDefinition).when(indexInitializer).buildWildcardTextIndexDefinition();
+
+			indexInitializer.initWildcardTextIndex();
+
+			verify(indexOperations).ensureIndex(textIndexDefinition);
+		}
+	}
+
+	@Nested
+	class TestBuildWildcardTextIndexDefinition {
+
+		@Test
+		void shouldSetIndexName() {
+			var result = indexInitializer.buildWildcardTextIndexDefinition();
+
+			assertThat(result.getIndexOptions()).containsEntry("name", MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME);
+		}
+
+		@Test
+		void shouldSetDefaultLanguage() {
+			var result = indexInitializer.buildWildcardTextIndexDefinition();
+
+			assertThat(result.getIndexOptions()).containsEntry("default_language", MongodbFullTextSearchIndexInitializer.TEXT_INDEX_LANGUAGE);
+		}
+
+		@Test
+		void shouldCreateWildcardTextIndex() {
+			var result = indexInitializer.buildWildcardTextIndexDefinition();
+
+			assertThat(result.getIndexKeys()).containsEntry("$**", "text");
+		}
+	}
+}
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java
index 65228c43a6fffd553c3520cb7d08ab8a0de3c69a..185953de74bc5b83361db559720fb272c8712fe9 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java
@@ -80,18 +80,6 @@ class SearchServiceTest {
 		}
 	}
 
-	@DisplayName("Exists by id")
-	@Nested
-	class TestExistsById {
-
-		@Test
-		void shouldCallRepositoryExistsById() {
-			searchService.existsById(VorgangTestFactory.ID);
-
-			verify(repository).existsById(VorgangTestFactory.ID);
-		}
-	}
-
 	@DisplayName("Search")
 	@Nested
 	class TestSearch {
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..751afdf88ad5545f59f9838787490ddfec73a703
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java
@@ -0,0 +1,151 @@
+/*
+ * 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.vorgang.common.search;
+
+import static org.awaitility.Awaitility.*;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import org.assertj.core.api.Assertions;
+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.ValueSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.index.IndexInfo;
+
+import de.ozgcloud.common.test.DataITCase;
+import de.ozgcloud.vorgang.vorgang.AntragstellerTestFactory;
+import de.ozgcloud.vorgang.vorgang.EingangTestFactory;
+import de.ozgcloud.vorgang.vorgang.FilterCriteria;
+import de.ozgcloud.vorgang.vorgang.FindVorgangRequest;
+import de.ozgcloud.vorgang.vorgang.Vorgang;
+import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
+
+@SpringBootTest(properties = { "ozgcloud.mongodbsearch.enabled=true" })
+@DataITCase
+public class SearchVorgangCustomRepositoryMongoITCase {
+
+	@Autowired
+	private SearchVorgangCustomRepositoryMongo repostitory;
+
+	@Autowired
+	private MongoOperations mongoOperations;
+
+	@Nested
+	class TestSpecialCharacters {
+
+		@BeforeEach
+		void init() {
+			await().atMost(Duration.ofSeconds(10)).until(() -> getTextIndex().isPresent());
+			mongoOperations.remove(Vorgang.class).all();
+		}
+
+		@DisplayName("should find vorgang when vorgangName")
+		@ParameterizedTest(name = "contains {0}")
+		@ValueSource(strings = { "_", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "\\", "/", "<",
+				">", "$", "%", "&", "#", "@" })
+		void shouldFindInVorgangName(String character) {
+			var vorgangName = "AbC%sTest".formatted(character);
+			mongoOperations.save(VorgangTestFactory.createBuilder().name(vorgangName).build());
+
+			var vorgangHeaders = repostitory.searchBy(searchRequest(character));
+
+			Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first()
+					.extracting("name").isEqualTo(vorgangName);
+		}
+
+		@Test
+		void shouldFindInVorgangNummer() {
+			mongoOperations.save(VorgangTestFactory.create());
+
+			var vorgangHeaders = repostitory.searchBy(buildSearchRequest("VOR-GANG"));
+
+			Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first()
+					.extracting("nummer").isEqualTo(VorgangTestFactory.VORGANG_NUMMER);
+		}
+
+		@Test
+		void shouldFindInAntragstellerName() {
+			var antragsteller = AntragstellerTestFactory.createBuilder().nachname("MusterMann").build();
+			var eingang = EingangTestFactory.createBuilder().antragsteller(antragsteller).build();
+			mongoOperations.save(VorgangTestFactory.createBuilder().clearEingangs().eingang(eingang).build());
+
+			var vorgangHeaders = repostitory.searchBy(buildSearchRequest("mustermann"));
+
+			Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1);
+		}
+
+		@Test
+		void shouldFindInAntragstellerVorname() {
+			var antragsteller = AntragstellerTestFactory.createBuilder().vorname("Alexander").build();
+			var eingang = EingangTestFactory.createBuilder().antragsteller(antragsteller).build();
+			mongoOperations.save(VorgangTestFactory.createBuilder().clearEingangs().eingang(eingang).build());
+
+			var vorgangHeaders = repostitory.searchBy(buildSearchRequest("alexand"));
+
+			Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1);
+		}
+
+		@Test
+		void shouldFindInAktenzeichen() {
+			var foundVorgang = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).aktenzeichen("123-456").build());
+			mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build());
+
+			var vorgangHeaders = repostitory.searchBy(buildSearchRequest("123-"));
+
+			Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first().extracting("id").isEqualTo(foundVorgang.getId());
+		}
+
+		@Test
+		void shouldFindInMultipleFields() {
+			var antragsteller = AntragstellerTestFactory.createBuilder().nachname("Müller").build();
+			mongoOperations.save(VorgangTestFactory.createBuilder().id(null).name("Mäuse")
+							.eingang(EingangTestFactory.createBuilder().antragsteller(antragsteller).build())
+							.build(), Vorgang.COLLECTION_NAME);
+
+			var result = repostitory.searchBy(buildSearchRequest("mull mau"));
+
+			Assertions.assertThat(result.getContent()).hasSize(1);
+		}
+
+		private Optional<IndexInfo> getTextIndex() {
+			return mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo().stream()
+					.filter(index -> MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME.equals(index.getName())).findFirst();
+		}
+
+		private FindVorgangRequest searchRequest(String character) {
+			return FindVorgangRequest.builder().filterBy(FilterCriteria.builder().build()).searchBy("abc%st".formatted(character)).build();
+		}
+
+		private FindVorgangRequest buildSearchRequest(String searchBy) {
+			return FindVorgangRequest.builder().filterBy(FilterCriteria.builder().build()).searchBy(searchBy).build();
+		}
+	}
+}
diff --git a/vorgang-manager-utils/pom.xml b/vorgang-manager-utils/pom.xml
index 920c88119e82ddd5b6e6717b8b12bfabfe9d5a0d..8e78361b0e1cae1ecef07ee4a41cf1e1ed1b0315 100644
--- a/vorgang-manager-utils/pom.xml
+++ b/vorgang-manager-utils/pom.xml
@@ -30,14 +30,14 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-dependencies</artifactId>
-		<version>4.2.0</version>
+		<version>4.3.0</version>
 		<relativePath/>
 	</parent>
 
 	<groupId>de.ozgcloud.vorgang</groupId>
 	<artifactId>vorgang-manager-utils</artifactId>
 	<name>OZG-Cloud Vorgang Manager Utils</name>
-	<version>2.9.0-SNAPSHOT</version>
+	<version>2.11.0-SNAPSHOT</version>
 
 	<properties>
 		<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
diff --git a/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java b/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java
index e2d04eb7aa6802d558e320c719f574a1fd27c142..26ce9a4bfd51d6599523467afd695711263b441f 100644
--- a/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java
+++ b/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java
@@ -9,6 +9,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Predicate;
 
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -17,6 +18,11 @@ import de.ozgcloud.vorgang.vorgang.GrpcFormField;
 
 class FormDataMappingUtils {
 
+	private static final Predicate<Object> IS_VALUE = obj -> obj instanceof String
+			|| obj instanceof Number
+			|| obj instanceof LocalDate
+			|| obj instanceof Boolean;
+
 	static final String VALUE_KEY = "value";
 	static final String LABEL_KEY = "label";
 	static final String CONTROL_DATA_KEY = "controlData";
@@ -72,13 +78,9 @@ class FormDataMappingUtils {
 
 	static boolean isValue(Object value) {
 		if (value instanceof Map<?, ?> valueMap) {
-			return valueMap.containsKey(VALUE_KEY) &&
-					(valueMap.get(VALUE_KEY) instanceof String
-							|| valueMap.get(VALUE_KEY) instanceof Number
-							|| valueMap.get(VALUE_KEY) instanceof LocalDate
-							|| valueMap.get(VALUE_KEY) instanceof Boolean);
+			return valueMap.containsKey(VALUE_KEY) && IS_VALUE.test(valueMap.get(VALUE_KEY));
 		} else {
-			return value instanceof String;
+			return IS_VALUE.test(value);
 		}
 	}
 
diff --git a/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java b/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java
index 3e9143321168141832df7261dc8b3069c5606acb..7694a8c144e0af1fd16d10ad76accb914103e471 100644
--- a/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java
+++ b/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java
@@ -115,6 +115,13 @@ class GrpcFormDataMapperTest {
 
 				assertThat(grpcField.getLabel()).isEqualTo(FIELD_LABEL);
 			}
+
+			@Test
+			void shouldHaveNumeric() {
+				var grpcField = mapper.mapToField(FIELD_NAME, Map.of(VALUE_KEY, 42, LABEL_KEY, FIELD_LABEL));
+
+				assertThat(grpcField.getValue()).isEqualTo("42");
+			}
 		}
 	}
 
@@ -167,7 +174,6 @@ class GrpcFormDataMapperTest {
 
 		@Test
 		void shouldHaveSubFormField() {
-
 			GrpcFormData formData = mapper.mapToFormData(Map.of("key", Map.of("subKey", "value")));
 
 			assertThat(formData.getForm(0).getFieldList()).hasSize(1);
@@ -175,6 +181,15 @@ class GrpcFormDataMapperTest {
 			assertThat(formData.getForm(0).getField(0).getName()).isEqualTo("subKey");
 			assertThat(formData.getForm(0).getField(0).getValue()).isEqualTo("value");
 		}
+
+		@Test
+		void shouldHaveNumericSubFormField() {
+			GrpcFormData formData = mapper.mapToFormData(Map.of("key", Map.of("subKey", 42)));
+
+			assertThat(formData.getForm(0).getFieldList()).hasSize(1);
+			assertThat(formData.getForm(0).getField(0).getValue()).isEqualTo("42");
+		}
+
 	}
 
 	@DisplayName("Mapped SubFormField")