diff --git a/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapter.java b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapter.java
index b3e75f0cd900dd24d9095c7953939ae450d5db5c..4d6d7a40ef312dc1f78f5c74721055e7b164f577 100644
--- a/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapter.java
+++ b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapter.java
@@ -8,6 +8,8 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
 
 import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -17,11 +19,12 @@ import org.springframework.stereotype.Component;
 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.FormHeader;
 import de.ozgcloud.eingang.common.formdata.IncomingFile;
 import de.ozgcloud.eingang.common.formdata.ServiceKonto;
 import de.ozgcloud.eingang.common.formdata.ZustaendigeStelle;
-import de.ozgcloud.eingang.semantik.enginebased.EngineBasedSemantikAdapter;
 import de.ozgcloud.eingang.semantik.common.ServiceKontoFactory;
+import de.ozgcloud.eingang.semantik.enginebased.EngineBasedSemantikAdapter;
 import lombok.NonNull;
 import lombok.extern.log4j.Log4j2;
 
@@ -35,6 +38,10 @@ public class DFoerdermittelEngineBasedSemantikAdapter implements EngineBasedSema
 	private static final String KEY_FACHNACHRICHT = "Fachnachricht";
 	private static final String KEY_POSTFACH_ID = "InboxReference";
 	private static final String KEY_ORGANISATIONS_EINHEIT_ID = "MetaText1";
+	private static final String KEY_FORM_NAME = "Title";
+	private static final String KEY_FORM_ID = "ProjectTitle";
+
+	static final String DEFAULT_FORM_NAME = "dFördermittelantrag: Ohne Titel";
 
 	@Autowired
 	private ServiceKontoFactory serviceKontoFactory;
@@ -48,27 +55,49 @@ public class DFoerdermittelEngineBasedSemantikAdapter implements EngineBasedSema
 	}
 
 	FormData processFachnachricht(FormData formData) {
-		@SuppressWarnings("unchecked")
-		Map<String, Object> fachnachricht = (Map<String, Object>) MapUtils.getMap(formData.getFormData(), KEY_FACHNACHRICHT,
-				Collections.<String, Object>emptyMap());
+		return pipe(
+				Stream.of(
+						this::addFormName,
+						this::addFormEngineName,
+						this::addServiceKonto,
+						this::addOrganisationsEinheitId,
+						this::addFormId
+				)
+		).apply(formData);
+	}
+
+	private UnaryOperator<FormData> pipe(Stream<UnaryOperator<FormData>> functions) {
+		return (formData) -> functions
+				.reduce(formData, (acc, f) -> f.apply(acc), (a, b) -> a);
+	}
+
+	FormData addFormId(FormData formData) {
+		return getNonEmptyFachnachrichtValueByKey(formData, KEY_FORM_ID)
+				.map(formId -> mapWithModifiedHeader(formData, headerBuilder -> headerBuilder.formId(formId)))
+				.orElse(formData);
+	}
 
-		var extendedFormData = addFormName(formData);
-		extendedFormData = addFormEngineName(extendedFormData);
-		extendedFormData = addServiceKonto(extendedFormData, fachnachricht);
-		return addOrganisationsEinheitId(extendedFormData, fachnachricht);
+	private Optional<String> getNonEmptyFachnachrichtValueByKey(FormData formData, String key) {
+		return Optional.ofNullable((String) getFachnachricht(formData).get(key))
+				.filter(StringUtils::isNotBlank);
 	}
 
-	FormData addServiceKonto(FormData formData, Map<String, Object> fachnachricht) {
-		return Optional.ofNullable((String) fachnachricht.get(KEY_POSTFACH_ID))
+	private FormData mapWithModifiedHeader(FormData formData, UnaryOperator<FormHeader.FormHeaderBuilder> headerBuilderOperator) {
+		return formData.toBuilder()
+				.header(headerBuilderOperator.apply(formData.getHeader().toBuilder()).build())
+				.build();
+	}
+
+	FormData addServiceKonto(FormData formData) {
+		return Optional.ofNullable((String) getFachnachricht(formData).get(KEY_POSTFACH_ID))
 				.map(this::extractPrefix)
 				.map(this::createServiceKonto)
-				.map(serviceKonto -> formData.getHeader().toBuilder().serviceKonto(serviceKonto).build())
-				.map(header -> formData.toBuilder().header(header).build())
+				.map(serviceKonto -> mapWithModifiedHeader(formData, headerBuilder -> headerBuilder.serviceKonto(serviceKonto)))
 				.orElse(formData);
 	}
 
-	FormData addOrganisationsEinheitId(FormData formData, Map<String, Object> fachnachricht) {
-		return Optional.ofNullable((String) fachnachricht.get(KEY_ORGANISATIONS_EINHEIT_ID))
+	FormData addOrganisationsEinheitId(FormData formData) {
+		return Optional.ofNullable((String) getFachnachricht(formData).get(KEY_ORGANISATIONS_EINHEIT_ID))
 				.map(orgaId -> addOrganisationsEinheitId(orgaId, formData.getZustaendigeStelle()))
 				.map(zustStelle -> formData.toBuilder().zustaendigeStelle(zustStelle).build())
 				.orElse(formData);
@@ -86,22 +115,19 @@ public class DFoerdermittelEngineBasedSemantikAdapter implements EngineBasedSema
 	}
 
 	FormData addFormName(FormData formData) {
-		return formData.toBuilder()
-				.header(formData.getHeader().toBuilder()
-						// TODO replace formName with actual name <Title> from Fachnachricht (KOP-2239)
-						.formName("dFördermittelantrag")
-						.build()
-				)
-				.build();
+		return mapWithModifiedHeader(formData, headerBuilder -> headerBuilder.formName(
+				getNonEmptyFachnachrichtValueByKey(formData, KEY_FORM_NAME)
+						.orElse(DEFAULT_FORM_NAME)
+		));
+	}
+
+	@SuppressWarnings("unchecked")
+	private Map<String, Object> getFachnachricht(FormData formData) {
+		return (Map<String, Object>) MapUtils.getMap(formData.getFormData(), KEY_FACHNACHRICHT, Collections.<String, Object>emptyMap());
 	}
 
 	FormData addFormEngineName(FormData formData) {
-		return formData.toBuilder()
-				.header(formData.getHeader().toBuilder()
-						.formEngineName("dFördermittelantrag")
-						.build()
-				)
-				.build();
+		return mapWithModifiedHeader(formData, headerBuilder -> headerBuilder.formEngineName("dFördermittelantrag"));
 	}
 
 	String extractPrefix(@NonNull String postfachId) {
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapterTest.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapterTest.java
index 05473793842bc54d734988d7cf1ad872d5789b7a..110e80b7d0a2f055a9624458e3ae15594ef87d86 100644
--- a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapterTest.java
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelEngineBasedSemantikAdapterTest.java
@@ -1,5 +1,7 @@
 package de.ozgcloud.eingang.semantik.enginebased.dfoerdermittel;
 
+import static de.ozgcloud.eingang.semantik.enginebased.dfoerdermittel.DFoerdermittelEngineBasedSemantikAdapter.*;
+import static de.ozgcloud.eingang.semantik.enginebased.dfoerdermittel.DFoerdermittelFormDataTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.assertj.core.api.InstanceOfAssertFactories.*;
 import static org.mockito.ArgumentMatchers.*;
@@ -9,6 +11,7 @@ import java.util.Collections;
 import java.util.Map;
 
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
@@ -19,6 +22,7 @@ import de.ozgcloud.common.binaryfile.TempFileUtils;
 import de.ozgcloud.common.test.TestUtils;
 import de.ozgcloud.eingang.common.formdata.FormData;
 import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.common.formdata.FormHeaderTestFactory;
 import de.ozgcloud.eingang.common.formdata.IncomingFile;
 import de.ozgcloud.eingang.common.formdata.PostfachAddressTestFactory;
 import de.ozgcloud.eingang.common.formdata.ServiceKontoTestFactory;
@@ -150,22 +154,23 @@ class DFoerdermittelEngineBasedSemantikAdapterTest {
 		@Mock
 		FormData formData4;
 
+		@Mock
+		FormData formData5;
+
 		@BeforeEach
 		void mock() {
-			var formData = DFoerdermittelFormDataTestFactory.create();
-			var fachnachrichtMap = DFoerdermittelFormDataTestFactory.createFachnachrichtMap();
-			when(formData0.getFormData()).thenReturn(formData.getFormData());
 			doReturn(formData1).when(adapter).addFormName(formData0);
 			doReturn(formData2).when(adapter).addFormEngineName(formData1);
-			doReturn(formData3).when(adapter).addServiceKonto(formData2, fachnachrichtMap);
-			doReturn(formData4).when(adapter).addOrganisationsEinheitId(formData3, fachnachrichtMap);
+			doReturn(formData3).when(adapter).addServiceKonto(formData2);
+			doReturn(formData4).when(adapter).addOrganisationsEinheitId(formData3);
+			doReturn(formData5).when(adapter).addFormId(formData4);
 		}
 
 		@Test
 		void shouldReturn() {
 			var processedFormData = adapter.processFachnachricht(formData0);
 
-			assertThat(processedFormData).isEqualTo(formData4);
+			assertThat(processedFormData).isEqualTo(formData5);
 		}
 	}
 
@@ -179,8 +184,7 @@ class DFoerdermittelEngineBasedSemantikAdapterTest {
 
 		@Test
 		void shouldHaveServiceKonto() {
-			var formData = adapter.addServiceKonto(DFoerdermittelFormDataTestFactory.create(),
-					DFoerdermittelFormDataTestFactory.createFachnachrichtMap());
+			var formData = adapter.addServiceKonto(DFoerdermittelFormDataTestFactory.create());
 
 			assertThat(formData.getHeader().getServiceKonto().getPostfachAddresses().get(0).getIdentifier())
 					.asInstanceOf(type(StringBasedIdentifier.class)).extracting(StringBasedIdentifier::getPostfachId)
@@ -189,7 +193,7 @@ class DFoerdermittelEngineBasedSemantikAdapterTest {
 
 		@Test
 		void shouldRemovePrefix() {
-			adapter.addServiceKonto(DFoerdermittelFormDataTestFactory.create(), DFoerdermittelFormDataTestFactory.createFachnachrichtMap());
+			adapter.addServiceKonto(DFoerdermittelFormDataTestFactory.create());
 
 			verify(serviceKontoFactory).buildOsiServiceKonto(DFoerdermittelFormDataTestFactory.POSTFACH_ID);
 		}
@@ -216,22 +220,76 @@ class DFoerdermittelEngineBasedSemantikAdapterTest {
 	class TestAddOrganisationsEinheitId {
 		@Test
 		void shouldHaveOrganisationsEinheitId() {
-			var formData = adapter.addOrganisationsEinheitId(DFoerdermittelFormDataTestFactory.create(),
-					DFoerdermittelFormDataTestFactory.createFachnachrichtMap());
+			var formData = adapter.addOrganisationsEinheitId(DFoerdermittelFormDataTestFactory.create());
 
 			assertThat(formData.getZustaendigeStelle()).isNotNull().extracting(ZustaendigeStelle::getOrganisationseinheitenId)
-					.isEqualTo(DFoerdermittelFormDataTestFactory.ORGANISATIONS_EINHEIT_ID);
+					.isEqualTo(ORGANISATIONS_EINHEIT_ID);
 		}
 	}
 
 	@Nested
 	class TestAddFormName {
 
+		@DisplayName("should have form name from title")
+		@Test
+		void shouldHaveFormNameFromTitle() {
+			var formData = adapter.addFormName(createFormDataWithTitle(FORM_NAME));
+
+			assertThat(formData.getHeader().getFormName()).isEqualTo(FORM_NAME);
+		}
+
+		@DisplayName("should have default form name for empty title")
+		@Test
+		void shouldHaveDefaultFormNameForEmptyTitle() {
+			var formData = adapter.addFormName(createFormDataWithTitle(""));
+
+			assertThat(formData.getHeader().getFormName()).isEqualTo(DEFAULT_FORM_NAME);
+		}
+
+		@DisplayName("should have default form name for missing title")
+		@Test
+		void shouldHaveDefaultFormNameForMissingTitle() {
+			var formData = adapter.addFormName(createFormDataWithTitle(null));
+
+			assertThat(formData.getHeader().getFormName()).isEqualTo(DEFAULT_FORM_NAME);
+		}
+
+		FormData createFormDataWithTitle(String title) {
+			return createFachnachrichtBuilder().title(title).build().createFormData();
+		}
+
+	}
+
+	@DisplayName("add form id")
+	@Nested
+	class TestAddFormId {
+
+		@DisplayName("should get form id from project title")
 		@Test
-		void shouldHaveFormName() {
-			var formData = adapter.addFormName(DFoerdermittelFormDataTestFactory.create());
+		void shouldGetFormIdFromProjectTitle() {
+			var formData = adapter.addFormId(createFormDataWithProjectTitle(FORM_ID));
+
+			assertThat(formData.getHeader().getFormId()).isEqualTo(FORM_ID);
+		}
+
+		@DisplayName("should keep form id if project title is empty")
+		@Test
+		void shouldKeepFormIdIfProjectTitleIsEmpty() {
+			var formData = adapter.addFormId(createFormDataWithProjectTitle(""));
+
+			assertThat(formData.getHeader().getFormId()).isEqualTo(FormHeaderTestFactory.FORM_ID);
+		}
+
+		@DisplayName("should keep form id if project title is null")
+		@Test
+		void shouldKeepFormIdIfProjectTitleIsNull() {
+			var formData = adapter.addFormId(createFormDataWithProjectTitle(null));
+
+			assertThat(formData.getHeader().getFormId()).isEqualTo(FormHeaderTestFactory.FORM_ID);
+		}
 
-			assertThat(formData.getHeader().getFormName()).isEqualTo("dFördermittelantrag");
+		FormData createFormDataWithProjectTitle(String projectTitle) {
+			return createFachnachrichtBuilder().projectTitle(projectTitle).build().createFormData();
 		}
 
 	}
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelFormDataTestFactory.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelFormDataTestFactory.java
index b99c66e48559d39b5c58e747c145a58dc0490363..8b8c9bf4103b13f085a7910361925a11e7b90297 100644
--- a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelFormDataTestFactory.java
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/dfoerdermittel/DFoerdermittelFormDataTestFactory.java
@@ -1,25 +1,74 @@
 package de.ozgcloud.eingang.semantik.enginebased.dfoerdermittel;
 
+import static java.util.stream.Collectors.*;
+
 import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.IntStream;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.FormHeaderTestFactory;
+import lombok.Builder;
 
 class DFoerdermittelFormDataTestFactory {
 
+	static final String FORM_NAME = "Name of form";
+	static final String FORM_ID = "Wert von ProjectTitle";
 	static final String POSTFACH_ID = "4dd01647-b9d9-4775-1b50-08da3d83800a";
 	static final String ORGANISATIONS_EINHEIT_ID = "9795669";
 
+	static Fachnachricht.FachnachrichtBuilder createFachnachrichtBuilder() {
+		return Fachnachricht.builder()
+				.title(FORM_NAME)
+				.projectTitle(FORM_ID)
+				.postfachId(POSTFACH_ID)
+				.organisationsEinheitId(ORGANISATIONS_EINHEIT_ID);
+	}
+
 	static FormData create() {
 		return createBuilder().build();
 	}
 
 	static FormData.FormDataBuilder createBuilder() {
+		return createBuilderWithFachnachricht(createFachnachrichtBuilder().build());
+	}
+
+	static FormData.FormDataBuilder createBuilderWithFachnachricht(Fachnachricht fachnachricht) {
 		return FormData.builder()
-				.formData(Map.of("Fachnachricht", createFachnachrichtMap()));
+				.header(FormHeaderTestFactory.create())
+				.formData(Map.of("Fachnachricht", createFachnachrichtMapWithFachnachricht(fachnachricht)));
 	}
 
 	static Map<String, Object> createFachnachrichtMap() {
-		return Map.of("InboxReference", "sh/sh/4dd01647-b9d9-4775-1b50-08da3d83800a",
-				"MetaText1", ORGANISATIONS_EINHEIT_ID);
+		return createFachnachrichtMapWithFachnachricht(createFachnachrichtBuilder().build());
+	}
+
+	static Map<String, Object> createFachnachrichtMapWithFachnachricht(Fachnachricht fachnachricht) {
+		return createMapWithValueFilter(
+				Objects::nonNull,
+				"Title", fachnachricht.title(),
+				"ProjectTitle", fachnachricht.projectTitle(),
+				"InboxReference", "sh/sh/" + fachnachricht.postfachId(),
+				"MetaText1", fachnachricht.organisationsEinheitId()
+		);
+	}
+
+	private static Map<String, Object> createMapWithValueFilter(Predicate<String> valuePredicate, String... keyValuePairs) {
+		if (keyValuePairs.length % 2 != 0) {
+			throw new IllegalArgumentException("Array length must be even to form key-value pairs.");
+		}
+
+		return IntStream.range(0, keyValuePairs.length / 2)
+				.boxed()
+				.filter(i -> valuePredicate.test(keyValuePairs[2 * i + 1]))
+				.collect(toMap(i -> keyValuePairs[2 * i], i -> keyValuePairs[2 * i + 1]));
+	}
+
+	@Builder
+	record Fachnachricht(String title, String projectTitle, String postfachId, String organisationsEinheitId) {
+		FormData createFormData() {
+			return createBuilderWithFachnachricht(this).build();
+		}
 	}
 }