diff --git a/common/src/main/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplier.java b/common/src/main/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplier.java
index 5d1468262c469c9595dabe39e5a7159a3f70a2de..510b7c92779fa39758cf3da4855a7af4dec20fe1 100644
--- a/common/src/main/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplier.java
+++ b/common/src/main/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplier.java
@@ -1,32 +1,26 @@
 package de.ozgcloud.eingang.common.vorgang;
 
-import java.time.Instant;
 import java.time.LocalDate;
 
+import org.apache.commons.lang3.RandomStringUtils;
 import org.springframework.stereotype.Component;
 
+import lombok.RequiredArgsConstructor;
+
 @Component
+@RequiredArgsConstructor
 public class VorgangNummerSupplier {
 
-	static final String BASE30_ALPHABET = "23456789ABCDEFGHJKMNPQRSTVWXYZ";
+	static final String VORGANGNUMMER_TEMPLATE = "%d%X%02d-%s";
+	static final char[] BASE30_ALPHABET = { '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M',
+			'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' };
 	static final int SUFFIX_LENGTH = 6;
 
 	public String get() {
-		var resultBuilder = initWithPrefix();
-		long currentTimeSeconds = Instant.now().toEpochMilli();
-		for (int i = 0; i < SUFFIX_LENGTH; i++) {
-			resultBuilder.append(BASE30_ALPHABET.charAt((int) (currentTimeSeconds % 30)));
-			currentTimeSeconds /= 30;
-		}
-		return resultBuilder.toString();
-	}
-
-	StringBuilder initWithPrefix() {
 		var today = LocalDate.now();
 		var lastYearNumber = today.getYear() % 10;
-		var monthValue = "%02d".formatted(today.getMonthValue());
-		var dayValue = "%02d".formatted(today.getDayOfMonth());
-		return new StringBuilder().append(lastYearNumber).append(monthValue).append(dayValue).append("-");
+		return VORGANGNUMMER_TEMPLATE.formatted(lastYearNumber, today.getMonthValue(), today.getDayOfMonth(),
+				RandomStringUtils.random(SUFFIX_LENGTH, BASE30_ALPHABET));
 	}
 
 }
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplierTest.java b/common/src/test/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplierTest.java
index d474cd190edfb39454d96c98a67d2d1a0886690d..2fc9a524f5e23d2bebd9721eb5612d96ad879782 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplierTest.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/vorgang/VorgangNummerSupplierTest.java
@@ -4,12 +4,15 @@ import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 import java.time.LocalDate;
+import java.util.Random;
 
+import org.apache.commons.lang3.RandomStringUtils;
 import org.junit.jupiter.api.DisplayName;
-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.test.util.ReflectionTestUtils;
 
 class VorgangNummerSupplierTest {
 
@@ -17,71 +20,67 @@ class VorgangNummerSupplierTest {
 	@InjectMocks
 	private VorgangNummerSupplier vorgangNummerSupplier;
 
-	@Nested
-	@DisplayName("Get Vorgang Nummer")
-	class TestGetVorgangNummer {
 
-		@Test
-		void shouldCallAddPrefix() {
-			vorgangNummerSupplier.get();
-
-			verify(vorgangNummerSupplier).initWithPrefix();
-		}
+	@Test
+	@DisplayName("should add random suffix of length 6")
+	void shouldAddSuffix() {
+		var result = vorgangNummerSupplier.get();
 
-		@Test
-		@DisplayName("should add random suffix of length 6")
-		void shouldAddSuffix() {
-			doReturn(new StringBuilder()).when(vorgangNummerSupplier).initWithPrefix();
+		assertThat(result).hasSize(11);
+	}
 
-			var result = vorgangNummerSupplier.get();
+	@Test
+	void shouldCallGetRandomString() {
+		try (var randomStringUtils = mockStatic(RandomStringUtils.class)) {
+			vorgangNummerSupplier.get();
 
-			assertThat(result).hasSize(6);
+			randomStringUtils.verify(() -> RandomStringUtils.random(VorgangNummerSupplier.SUFFIX_LENGTH, VorgangNummerSupplier.BASE30_ALPHABET));
 		}
 	}
 
-	@Nested
-	class TestAddPrefix {
+	@Test
+	void shouldHaveSize() {
+		var result = vorgangNummerSupplier.get();
 
-		@Test
-		void shouldHaveSize() {
-			var resultBuilder = vorgangNummerSupplier.initWithPrefix();
+		assertThat(getPrefix(result)).hasSize(5);
+	}
 
-			assertThat(resultBuilder).hasSize(6);
-		}
+	private String getPrefix(String string) {
+		return string.substring(0, string.indexOf('-') + 1);
+	}
 
-		@Test
-		void shouldAddLastYearNumberFirst() {
-			var lastYearNumber = "" + LocalDate.now().getYear() % 10;
+	@Test
+	void shouldAddLastYearNumberFirst() {
+		var lastYearNumber = "" + LocalDate.now().getYear() % 10;
 
-			var resultBuilder = vorgangNummerSupplier.initWithPrefix();
+		var result = vorgangNummerSupplier.get();
 
-			assertThat(resultBuilder.substring(0, 1)).isEqualTo(lastYearNumber);
-		}
+		assertThat(result.substring(0, 1)).isEqualTo(lastYearNumber);
+	}
 
-		@Test
-		void shouldAddMonthValueSecond() {
-			var monthValue = "%02d".formatted(LocalDate.now().getMonthValue());
+	@Test
+	void shouldAddMonthValueSecond() {
+		var monthHexValue = "%X".formatted(LocalDate.now().getMonthValue());
 
-			var resultBuilder = vorgangNummerSupplier.initWithPrefix();
+		var result = vorgangNummerSupplier.get();
 
-			assertThat(resultBuilder.substring(1, 3)).isEqualTo(monthValue);
-		}
+		assertThat(result.substring(1, 2)).isEqualTo(monthHexValue);
+	}
 
-		@Test
-		void shouldAddDayValueThird() {
-			var dayValue = "%02d".formatted(LocalDate.now().getDayOfMonth());
+	@Test
+	void shouldAddDayValueThird() {
+		var dayValue = "%02d".formatted(LocalDate.now().getDayOfMonth());
 
-			var resultBuilder = vorgangNummerSupplier.initWithPrefix();
+		var result = vorgangNummerSupplier.get();
 
-			assertThat(resultBuilder.substring(3, 5)).isEqualTo(dayValue);
-		}
+		assertThat(result.substring(2, 4)).isEqualTo(dayValue);
+	}
 
-		@Test
-		void shouldAddHyphenAtEnd() {
-			var resultBuilder = vorgangNummerSupplier.initWithPrefix();
+	@Test
+	void shouldAddHyphenAtEnd() {
+		var result = vorgangNummerSupplier.get();
 
-			assertThat(resultBuilder.charAt(5)).isEqualTo('-');
-		}
+		assertThat(result.charAt(4)).isEqualTo('-');
 	}
 
 }
\ No newline at end of file
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/main/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandler.java b/formcycle-adapter/formcycle-adapter-impl/src/main/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandler.java
index c3977ab98b49d2abc6cc61d363549179ad6e6e7e..d98bdc6aaf6a18ee25015f577945b356205ae3a2 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/main/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandler.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/main/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandler.java
@@ -21,6 +21,8 @@ public class FormcycleExceptionHandler extends ResponseEntityExceptionHandler {
 	static final String CREATE_VORGANG_EXCEPTION_MESSAGE = "Cannot create vorgang.";
 	static final String UNEXPECTED_EXCEPTION_MESSAGE = "An unexpected error occurred.";
 
+	private static final String EXCEPTION_ID_TEMPLATE = "(ExceptionId:";
+
 	@ExceptionHandler({ TechnicalException.class })
 	public ResponseEntity<InternalExceptionDto> handleTechnicalException(TechnicalException e, WebRequest request) {
 		LOG.error(TECHNICAL_EXCEPTION_MESSAGE, e);
@@ -29,12 +31,25 @@ public class FormcycleExceptionHandler extends ResponseEntityExceptionHandler {
 
 	@ExceptionHandler({ StatusRuntimeException.class })
 	public ResponseEntity<InternalExceptionDto> handleStatusRuntimeException(StatusRuntimeException e, WebRequest request) {
-		LOG.error(CREATE_VORGANG_EXCEPTION_MESSAGE, e);
-		return buildResponseEntity(CREATE_VORGANG_EXCEPTION_MESSAGE, parseExceptionId(e.getMessage()));
+		var logMessage = TECHNICAL_EXCEPTION_MESSAGE;
+		var exceptionId = getExceptionId(e.getMessage());
+		if (!hasExceptionId(e.getMessage())) {
+			logMessage = ExceptionUtil.formatMessageWithExceptionId(CREATE_VORGANG_EXCEPTION_MESSAGE, exceptionId);
+		}
+		LOG.error(logMessage, e);
+		return buildResponseEntity(CREATE_VORGANG_EXCEPTION_MESSAGE, exceptionId);
+	}
+
+	boolean hasExceptionId(String message) {
+		return message.contains(EXCEPTION_ID_TEMPLATE);
 	}
 
-	String parseExceptionId(String message) {
-		return message.substring(message.indexOf("(ExceptionId:") + 14, message.indexOf(")"));
+	String getExceptionId(String message) {
+		try {
+			return message.substring(message.indexOf(EXCEPTION_ID_TEMPLATE) + 14, message.indexOf(")"));
+		} catch (IndexOutOfBoundsException e) {
+			return createExceptionId();
+		}
 	}
 
 	@ExceptionHandler({ RuntimeException.class })
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandlerTest.java b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandlerTest.java
index b4e9053c14a5e5a791262a76a0858ee919e3cfe2..f5e8ffaf282d688e800ab64cd87d57a478c0fd81 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandlerTest.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/FormcycleExceptionHandlerTest.java
@@ -1,9 +1,11 @@
 package de.ozgcloud.eingang.formcycle.common.errorhandling;
 
-import static de.ozgcloud.eingang.formcycle.common.errorhandling.InternalExceptinDtoTestFactory.*;
+import static de.ozgcloud.eingang.formcycle.common.errorhandling.InternalExceptionDtoTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
+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;
@@ -12,6 +14,7 @@ import org.mockito.Spy;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 
+import de.ozgcloud.common.errorhandling.ExceptionUtil;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import io.grpc.StatusRuntimeException;
 
@@ -46,35 +49,31 @@ class FormcycleExceptionHandlerTest {
 
 		@Test
 		void shouldCallBuildResponseEntity() {
-			doReturn(EXCEPTION_ID).when(exceptionHandler).parseExceptionId(any());
+			when(statusRuntimeException.getStackTrace()).thenReturn(new StackTraceElement[0]);
+			when(statusRuntimeException.getMessage()).thenReturn(ExceptionUtil.formatMessageWithExceptionId(MESSAGE, EXCEPTION_ID));
 
 			exceptionHandler.handleStatusRuntimeException(statusRuntimeException, null);
 
 			verify(exceptionHandler).buildResponseEntity(FormcycleExceptionHandler.CREATE_VORGANG_EXCEPTION_MESSAGE, EXCEPTION_ID);
 		}
 
-		@Test
-		void shouldParseExceptionId() {
-			var parseExceptionId = exceptionHandler.parseExceptionId(InternalExceptinDtoTestFactory.messageWithExceptionId());
-
-			assertThat(parseExceptionId).isEqualTo(EXCEPTION_ID);
-		}
-
-	}
+		@Nested
+		class TestGetExceptionId {
 
-	@Nested
-	class TestHandleUnexpectedException {
+			@Test
+			void shouldReturnExceptionIdFromMessage() {
+				var exceptionId = exceptionHandler.getExceptionId(messageWithExceptionId());
 
-		private static final String EXCEPTION_MESSAGE = "Test";
+				assertThat(exceptionId).isEqualTo(EXCEPTION_ID);
+			}
 
-		@Test
-		void shouldCallBuildResponseEntity() {
-			when(exceptionHandler.createExceptionId()).thenReturn(EXCEPTION_ID);
+			@Test
+			void shouldCreateNewExceptionId() {
+				var exceptionId = exceptionHandler.getExceptionId(MESSAGE);
 
-			exceptionHandler.handleUnexpectedException(new RuntimeException(EXCEPTION_MESSAGE), null);
+				assertThat(exceptionId).isNotEqualTo(EXCEPTION_ID);
+			}
 
-			verify(exceptionHandler).buildResponseEntity(FormcycleExceptionHandler.UNEXPECTED_EXCEPTION_MESSAGE,
-					EXCEPTION_ID);
 		}
 
 	}
@@ -93,11 +92,11 @@ class FormcycleExceptionHandlerTest {
 		void shouldCallBuildInternalExceptionDto() {
 			buildResponseEntity();
 
-			verify(exceptionHandler).buildInternalExceptionDto(InternalExceptinDtoTestFactory.MESSAGE, EXCEPTION_ID);
+			verify(exceptionHandler).buildInternalExceptionDto(InternalExceptionDtoTestFactory.MESSAGE, EXCEPTION_ID);
 		}
 
 		private ResponseEntity<InternalExceptionDto> buildResponseEntity() {
-			return exceptionHandler.buildResponseEntity(InternalExceptinDtoTestFactory.MESSAGE, EXCEPTION_ID);
+			return exceptionHandler.buildResponseEntity(InternalExceptionDtoTestFactory.MESSAGE, EXCEPTION_ID);
 		}
 
 	}
@@ -116,11 +115,11 @@ class FormcycleExceptionHandlerTest {
 		void shouldSetMessage() {
 			var response = buildInternalExceptionDto();
 
-			assertThat(response.getMessage()).isEqualTo(InternalExceptinDtoTestFactory.MESSAGE);
+			assertThat(response.getMessage()).isEqualTo(InternalExceptionDtoTestFactory.MESSAGE);
 		}
 
 		private InternalExceptionDto buildInternalExceptionDto() {
-			return exceptionHandler.buildInternalExceptionDto(InternalExceptinDtoTestFactory.MESSAGE, EXCEPTION_ID);
+			return exceptionHandler.buildInternalExceptionDto(InternalExceptionDtoTestFactory.MESSAGE, EXCEPTION_ID);
 		}
 
 	}
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptinDtoTestFactory.java b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptionDtoTestFactory.java
similarity index 93%
rename from formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptinDtoTestFactory.java
rename to formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptionDtoTestFactory.java
index 17121a3964962a7b65ff50f1ed5f1729f1f03640..98a8e5f27c53fd8676f1233ae24e3bf7c42306eb 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptinDtoTestFactory.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/common/errorhandling/InternalExceptionDtoTestFactory.java
@@ -3,7 +3,7 @@ package de.ozgcloud.eingang.formcycle.common.errorhandling;
 import de.ozgcloud.common.errorhandling.ExceptionUtil;
 import de.ozgcloud.eingang.formcycle.common.errorhandling.InternalExceptionDto.InternalExceptionDtoBuilder;
 
-public class InternalExceptinDtoTestFactory {
+public class InternalExceptionDtoTestFactory {
 
 	public static final String EXCEPTION_ID = "exception-id";
 	public static final String MESSAGE = "exception message";
diff --git a/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/CustomHeaderReader.java b/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/CustomHeaderReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..dad25e9f009aa064cc79e5837ccf4687d0dcdc89
--- /dev/null
+++ b/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/CustomHeaderReader.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.eingang.intelliform;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import org.springframework.stereotype.Component;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+
+@Component
+public class CustomHeaderReader {
+
+	public static final String HEADER_POSTFACH_ID = "u:saml_legacypostkorbhandle";
+	public static final String HEADER_VORNAME = "u:saml_givenname";
+	public static final String HEADER_NACHNAME = "u:saml_surname";
+	public static final String HEADER_GEBURTSORT = "u:saml_placeofbirth";
+	public static final String HEADER_GEBURTSNAME = "u:saml_birthname";
+	public static final String HEADER_EMAIL = "u:saml_mail";
+	public static final String HEADER_TELEFON = "u:saml_telephonenumber";
+	public static final String HEADER_STRASSE = "u:saml_postaladdress";
+	public static final String HEADER_PLZ = "u:saml_postalcode";
+	public static final String HEADER_ORT = "u:saml_localityname";
+
+	public Map<String, Object> getHeader(Document document) {
+		var map = new HashMap<String, Object>();
+		Consumer<Attr> addHeader = attr -> map.put(attr.getName(), attr.getValue());
+		getAttribute(document, HEADER_POSTFACH_ID).ifPresent(addHeader);
+		getAttribute(document, HEADER_VORNAME).ifPresent(addHeader);
+		getAttribute(document, HEADER_NACHNAME).ifPresent(addHeader);
+		getAttribute(document, HEADER_GEBURTSNAME).ifPresent(addHeader);
+		getAttribute(document, HEADER_GEBURTSORT).ifPresent(addHeader);
+		getAttribute(document, HEADER_EMAIL).ifPresent(addHeader);
+		getAttribute(document, HEADER_TELEFON).ifPresent(addHeader);
+		getAttribute(document, HEADER_STRASSE).ifPresent(addHeader);
+		getAttribute(document, HEADER_PLZ).ifPresent(addHeader);
+		getAttribute(document, HEADER_ORT).ifPresent(addHeader);
+		return map;
+	}
+
+	Optional<Attr> getAttribute(Document document, String name) {
+		return Optional.ofNullable(document.getDocumentElement().getAttributeNode(name));
+	}
+}
\ No newline at end of file
diff --git a/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapper.java b/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapper.java
index 5d9faa593aa0a61b19dee676ab2a233106e010f3..f4796328f0f9469c17291a9f4dfda6655c1213cf 100644
--- a/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapper.java
+++ b/intelliform-adapter/src/main/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapper.java
@@ -86,6 +86,9 @@ class SemantikFormDataMapper {
 	@Autowired
 	private final AttachmentsContentAdder attachmentsContentAdder;
 
+	@Autowired
+	private final CustomHeaderReader customHeaderReader;
+
 	public FormData mapToFormData(Deposit depositData) {
 		var xmlFormDataStream = getXmlFormData(depositData.getData());
 
@@ -156,6 +159,7 @@ class SemantikFormDataMapper {
 		map.put(HEADER_CUSTOMER_ID, document.getDocumentElement().getAttribute(HEADER_CUSTOMER_ID));
 		map.put(HEADER_CLIENT, document.getDocumentElement().getAttribute(HEADER_CLIENT));
 		map.put(HEADER_CLIENT_ID, document.getDocumentElement().getAttribute(HEADER_CLIENT_ID));
+		map.putAll(customHeaderReader.getHeader(document));
 		return map;
 	}
 }
\ No newline at end of file
diff --git a/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderReaderTest.java b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderReaderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb435c85ecd0d0dc5214f7a595abc7bd9632f396
--- /dev/null
+++ b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderReaderTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.eingang.intelliform;
+
+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.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+class CustomHeaderReaderTest {
+
+	@InjectMocks
+	private CustomHeaderReader reader;
+
+	@Mock
+	private Document document;
+	@Mock
+	private Element element;
+	@Mock
+	private Attr attribute;
+
+	@BeforeEach
+	void setup() {
+		when(document.getDocumentElement()).thenReturn(element);
+	}
+
+	@Test
+	void shouldNotAddIfNotPresent() {
+		var formData = reader.getHeader(document);
+
+		assertThat(formData).isEmpty();
+	}
+
+	@Nested
+	class TestHeaderAdded {
+
+		@BeforeEach
+		void setup() {
+			when(element.getAttributeNode(any())).thenReturn(attribute);
+		}
+
+		@Test
+		void shouldAddPostfachId() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_POSTFACH_ID);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.POSTFACH_ID);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_POSTFACH_ID, CustomHeaderTestFactory.POSTFACH_ID);
+		}
+
+		@Test
+		void shouldAddVorname() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_VORNAME);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.VORNAME);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_VORNAME, CustomHeaderTestFactory.VORNAME);
+		}
+
+		@Test
+		void shouldAddNachname() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_NACHNAME);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.NACHNAME);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_NACHNAME, CustomHeaderTestFactory.NACHNAME);
+		}
+
+		@Test
+		void shouldAddGeburtsname() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_GEBURTSNAME);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.GEBURTSNAME);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_GEBURTSNAME, CustomHeaderTestFactory.GEBURTSNAME);
+		}
+
+		@Test
+		void shouldAddGeburtsort() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_GEBURTSORT);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.GEBURTSORT);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_GEBURTSORT, CustomHeaderTestFactory.GEBURTSORT);
+		}
+
+		@Test
+		void shouldAddEmail() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_EMAIL);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.EMAIL);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_EMAIL, CustomHeaderTestFactory.EMAIL);
+		}
+
+		@Test
+		void shouldAddTelefon() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_TELEFON);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.TELEFON);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_TELEFON, CustomHeaderTestFactory.TELEFON);
+		}
+
+		@Test
+		void shouldAddStrasse() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_STRASSE);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.STRASSE);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_STRASSE, CustomHeaderTestFactory.STRASSE);
+		}
+
+		@Test
+		void shouldAddPlz() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_PLZ);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.PLZ);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_PLZ, CustomHeaderTestFactory.PLZ);
+		}
+
+		@Test
+		void shouldAddOrt() {
+			when(attribute.getName()).thenReturn(CustomHeaderReader.HEADER_ORT);
+			when(attribute.getValue()).thenReturn(CustomHeaderTestFactory.ORT);
+
+			var formData = reader.getHeader(document);
+
+			assertThat(formData).containsEntry(CustomHeaderReader.HEADER_ORT, CustomHeaderTestFactory.ORT);
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderTestFactory.java b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..acb9fede61f17309e4417fac7149f5d03177c588
--- /dev/null
+++ b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/CustomHeaderTestFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+package de.ozgcloud.eingang.intelliform;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CustomHeaderTestFactory {
+
+	public static final String POSTFACH_ID = "postfach_id";
+	public static final String VORNAME = "vorname";
+	public static final String NACHNAME = "nachname";
+	public static final String GEBURTSNAME = "geburtsname";
+	public static final String GEBURTSORT = "geburtsort";
+	public static final String EMAIL = "email";
+	public static final String TELEFON = "telefon";
+	public static final String STRASSE = "strasse";
+	public static final String PLZ = "plz";
+	public static final String ORT = "ort";
+
+	public static Map<String, Object> create() {
+		var map = new HashMap<String, Object>();
+		map.put(CustomHeaderReader.HEADER_POSTFACH_ID, POSTFACH_ID);
+		map.put(CustomHeaderReader.HEADER_VORNAME, VORNAME);
+		map.put(CustomHeaderReader.HEADER_NACHNAME, NACHNAME);
+		map.put(CustomHeaderReader.HEADER_GEBURTSNAME, GEBURTSNAME);
+		map.put(CustomHeaderReader.HEADER_GEBURTSORT, GEBURTSORT);
+		map.put(CustomHeaderReader.HEADER_EMAIL, EMAIL);
+		map.put(CustomHeaderReader.HEADER_TELEFON, TELEFON);
+		map.put(CustomHeaderReader.HEADER_STRASSE, STRASSE);
+		map.put(CustomHeaderReader.HEADER_PLZ, PLZ);
+		map.put(CustomHeaderReader.HEADER_ORT, ORT);
+		return map;
+	}
+
+}
diff --git a/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapperTest.java b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapperTest.java
index 36f64310aaf25183df3acdb36f93567032e43576..3bf90315f3d0be4e23ea2de093908455ad32b18e 100644
--- a/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapperTest.java
+++ b/intelliform-adapter/src/test/java/de/ozgcloud/eingang/intelliform/SemantikFormDataMapperTest.java
@@ -25,6 +25,8 @@ package de.ozgcloud.eingang.intelliform;
 
 import static de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -33,8 +35,10 @@ import java.util.Map;
 
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
 import org.xml.sax.SAXException;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
@@ -43,8 +47,16 @@ import de.ozgcloud.eingang.semantik.enginebased.FilesMapperHelper;
 
 class SemantikFormDataMapperTest {
 
-	private SemantikFormDataMapper mapper = new SemantikFormDataMapper(new XmlToJavaMapsMapper(), new FormDataIncomingFileMapper(),
-			new RepresentationsCalculator(), new DepositRequestIncomingFileMapper(), new AttachmentsContentAdder());
+	private SemantikFormDataMapper mapper;
+
+	@Mock
+	private CustomHeaderReader customHeaderReader;
+
+	@BeforeEach
+	void setup() {
+		mapper = new SemantikFormDataMapper(new XmlToJavaMapsMapper(), new FormDataIncomingFileMapper(),
+				new RepresentationsCalculator(), new DepositRequestIncomingFileMapper(), new AttachmentsContentAdder(), customHeaderReader);
+	}
 
 	@Nested
 	class TestMapFormData {
@@ -136,6 +148,17 @@ class SemantikFormDataMapperTest {
 						.containsEntry(SemantikFormDataMapper.HEADER_CLIENT_ID, "land");
 			}
 
+			@Test
+			void shouldAddBayernHeader() {
+				Map<String, Object> bayernHeader = Map.of(CustomHeaderReader.HEADER_POSTFACH_ID, CustomHeaderTestFactory.POSTFACH_ID);
+				when(customHeaderReader.getHeader(any())).thenReturn(bayernHeader);
+
+				var formData = mapToFormData(deposit);
+
+				verify(customHeaderReader).getHeader(any());
+				assertThat(getHeader(formData)).containsEntry(CustomHeaderReader.HEADER_POSTFACH_ID, CustomHeaderTestFactory.POSTFACH_ID);
+			}
+
 			@SuppressWarnings("unchecked")
 			private Map<String, Object> getHeader(FormData formData) {
 				return (Map<String, Object>) formData.getFormData().get(SemantikFormDataMapper.HEADER_FIELD);
diff --git a/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapper.java b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3171846b129f4f676a7392d2f857e0f72714705c
--- /dev/null
+++ b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapper.java
@@ -0,0 +1,67 @@
+package de.ozgcloud.eingang.semantik.enginebased.afm;
+
+import static java.util.Objects.*;
+import static org.apache.commons.lang3.StringUtils.*;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.eingang.common.errorhandling.TechnicalException;
+import de.ozgcloud.eingang.common.formdata.Antragsteller;
+import de.ozgcloud.eingang.common.formdata.FormData;
+
+@Component
+public class AfmAntragstellerHeaderMapper {
+
+	static final String KEY_POSTFACH_ID = "u:saml_legacypostkorbhandle";
+	static final String KEY_VORNAME = "u:saml_givenname";
+	static final String KEY_NACHNAME = "u:saml_surname";
+	static final String KEY_GEBURTSORT = "u:saml_placeofbirth";
+	public static final String KEY_GEBURTSNAME = "u:saml_birthname";
+	static final String KEY_EMAIL = "u:saml_mail";
+	static final String KEY_TELEFON = "u:saml_telephonenumber";
+	static final String KEY_STRASSE = "u:saml_postaladdress";
+	static final String KEY_PLZ = "u:saml_postalcode";
+	static final String KEY_ORT = "u:saml_localityname";
+
+	public FormData parseAntragstellerData(FormData formData) {
+		return formData.toBuilder().antragsteller(buildAntragsteller(getHeaders(formData))).build();
+	}
+
+	Antragsteller buildAntragsteller(Map<String, Object> headers) {
+		return Antragsteller.builder()
+				.postfachId((String) headers.get(KEY_POSTFACH_ID))
+				.vorname((String) headers.get(KEY_VORNAME))
+				.nachname((String) headers.get(KEY_NACHNAME))
+				.geburtsname((String) headers.get(KEY_GEBURTSNAME))
+				.geburtsort((String) headers.get(KEY_GEBURTSORT))
+				.email((String) headers.get(KEY_EMAIL))
+				.telefon((String) headers.get(KEY_TELEFON))
+				.strasse((String) headers.get(KEY_STRASSE))
+				.plz((String) headers.get(KEY_PLZ))
+				.ort((String) headers.get(KEY_ORT))
+				.build();
+	}
+
+	public boolean isResponsible(FormData formData) {
+		var headers = getHeaders(formData);
+		return headers.containsKey(KEY_POSTFACH_ID) && isPostfachIdNotBlank(headers.get(KEY_POSTFACH_ID));
+	}
+
+	@SuppressWarnings("unchecked")
+	Map<String, Object> getHeaders(FormData formData) {
+		return (Map<String, Object>) formData.getFormData().getOrDefault(AfmHeaderMapper.HEADER_FIELD, Collections.emptyMap());
+	}
+
+	boolean isPostfachIdNotBlank(Object postfachId) {
+		if (isNull(postfachId)) {
+			return false;
+		}
+		if (postfachId instanceof String id) {
+			return isNotBlank(id);
+		}
+		throw new TechnicalException("Unexpected type of postfach id: " + postfachId.getClass().getName());
+	}
+}
diff --git a/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapper.java b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapper.java
index 68311943caa6e53772bfb97c3badb8994177d81e..4e5251da5812a678dd2234ab964de002cabd78e8 100644
--- a/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapper.java
+++ b/semantik-adapter/src/main/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapper.java
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import de.ozgcloud.eingang.common.formdata.Antragsteller;
@@ -56,8 +57,14 @@ class AfmAntragstellerMapper implements AfmEngineBasedMapper {
 	static final String PLZ = "sh_plz";
 	static final String ORT = "ort";
 
+	@Autowired
+	private AfmAntragstellerHeaderMapper antragstellerHeaderMapper;
+
 	@Override
 	public FormData parseFormData(FormData formData) {
+		if (antragstellerHeaderMapper.isResponsible(formData)) {
+			return antragstellerHeaderMapper.parseAntragstellerData(formData);
+		}
 		var formDataMap = formData.getFormData();
 		var builder = Antragsteller.builder().postfachId(getPostfachId(formDataMap));
 
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapperTest.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..953ff17d4427885712c3d95f1b9524b0b1dc1bc1
--- /dev/null
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerHeaderMapperTest.java
@@ -0,0 +1,241 @@
+package de.ozgcloud.eingang.semantik.enginebased.afm;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.ozgcloud.eingang.common.errorhandling.TechnicalException;
+import de.ozgcloud.eingang.common.formdata.AntragstellerTestFactory;
+import de.ozgcloud.eingang.common.formdata.FormData;
+
+class AfmAntragstellerHeaderMapperTest {
+
+	@Spy
+	@InjectMocks
+	private AfmAntragstellerHeaderMapper mapper;
+
+	@Nested
+	class TestParseAntragstellerData {
+
+		@Test
+		void shouldCallGetHeaders() {
+			var formData = FormData.builder().build();
+			doReturn(Collections.emptyMap()).when(mapper).getHeaders(any());
+
+			mapper.parseAntragstellerData(formData);
+
+			verify(mapper).getHeaders(formData);
+		}
+
+		@Test
+		void shouldCallBuildAntragsteller() {
+			var headerMap = AfmHeaderTestFactory.createCustomHeaderMap();
+			doReturn(headerMap).when(mapper).getHeaders(any());
+
+			mapper.parseAntragstellerData(FormData.builder().build());
+
+			verify(mapper).buildAntragsteller(headerMap);
+		}
+
+		@Test
+		void shouldSetAntragsteller() {
+			var antragsteller = AntragstellerTestFactory.create();
+			doReturn(antragsteller).when(mapper).buildAntragsteller(any());
+
+			var result = mapper.parseAntragstellerData(FormData.builder().build());
+
+			assertThat(result.getAntragsteller()).isEqualTo(antragsteller);
+		}
+
+		@Nested
+		class TestBuildAntragsteller {
+
+			private Map<String, Object> headers = AfmHeaderTestFactory.createCustomHeaderMap();
+			@Test
+			void shouldSetPostfachId() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getPostfachId()).isEqualTo(AfmHeaderTestFactory.CUSTOM_POSTFACH_ID);
+			}
+
+			@Test
+			void shouldSetVorname() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getVorname()).isEqualTo(AfmHeaderTestFactory.CUSTOM_VORNAME);
+			}
+
+			@Test
+			void shouldSetNachname() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getNachname()).isEqualTo(AfmHeaderTestFactory.CUSTOM_NACHNAME);
+			}
+
+			@Test
+			void shouldSetGeburtsname() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getGeburtsname()).isEqualTo(AfmHeaderTestFactory.CUSTOM_GEBURTSNAME);
+			}
+
+			@Test
+			void shouldSetGeburtsort() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getGeburtsort()).isEqualTo(AfmHeaderTestFactory.CUSTOM_GEBURTSORT);
+			}
+
+			@Test
+			void shoudlSetEmail() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getEmail()).isEqualTo(AfmHeaderTestFactory.CUSTOM_EMAIL);
+			}
+
+			@Test
+			void shouldSetTelefon() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getTelefon()).isEqualTo(AfmHeaderTestFactory.CUSTOM_TELEFON);
+			}
+
+			@Test
+			void shouldSetStrasse() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getStrasse()).isEqualTo(AfmHeaderTestFactory.CUSTOM_STRASSE);
+			}
+
+			@Test
+			void shouldSetPlz() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getPlz()).isEqualTo(AfmHeaderTestFactory.CUSTOM_PLZ);
+			}
+
+			@Test
+			void shouldSetOrt() {
+				var result = mapper.buildAntragsteller(headers);
+
+				assertThat(result.getOrt()).isEqualTo(AfmHeaderTestFactory.CUSTOM_ORT);
+			}
+		}
+	}
+
+	@Nested
+	class TestIsResponsible {
+
+		@Mock
+		private FormData formData;
+
+		@Test
+		void shouldApproveResponsibility() {
+			doReturn(true).when(mapper).isPostfachIdNotBlank(any());
+			doReturn(createHeaders(AfmAntragstellerHeaderMapper.KEY_POSTFACH_ID, "123")).when(mapper).getHeaders(any());
+
+			var isResponsible = mapper.isResponsible(formData);
+
+			assertTrue(isResponsible);
+		}
+
+		@Nested
+		class TestDenyResponsibility {
+
+			@Test
+			void shouldDenyWhenNoHeader() {
+				doReturn(Collections.emptyMap()).when(mapper).getHeaders(any());
+
+				var isResponsible = mapper.isResponsible(FormData.builder().build());
+
+				assertFalse(isResponsible);
+			}
+
+			@Test
+			void shouldDenyWhenNoPostfachId() {
+				doReturn(createHeaders(AfmAntragstellerHeaderMapper.KEY_VORNAME, "name")).when(mapper).getHeaders(any());
+
+				var isResponsible = mapper.isResponsible(formData);
+
+				assertFalse(isResponsible);
+			}
+
+			void shouldDenyWhenPostfachIdIsBlank() {
+				doReturn(createHeaders(AfmAntragstellerHeaderMapper.KEY_POSTFACH_ID, null)).when(mapper).getHeaders(any());
+				doReturn(false).when(mapper).isPostfachIdNotBlank(any());
+
+				var isResponsible = mapper.isResponsible(formData);
+
+				assertFalse(isResponsible);
+			}
+
+			@Nested
+			class TestIsPostfachIdNotBlank {
+
+				@ParameterizedTest
+				@NullAndEmptySource
+				void shouldReturnFalseWhenPostfachIdIsBlank(String postfachId) {
+					var isNotBlank = mapper.isPostfachIdNotBlank(postfachId);
+
+					assertFalse(isNotBlank);
+				}
+
+				@Test
+				void shouldApprove() {
+					var isNotBlank = mapper.isPostfachIdNotBlank("123");
+
+					assertTrue(isNotBlank);
+				}
+
+				@Test
+				void shouldFailOnUnexpectedType() {
+					var postfachId = new Object();
+
+					assertThrows(TechnicalException.class, () -> mapper.isPostfachIdNotBlank(postfachId));
+				}
+			}
+		}
+	}
+
+	@Nested
+	class TestGetHeaders {
+
+		@Test
+		void shouldReturnHeaders() {
+			var headers = createHeaders(AfmAntragstellerHeaderMapper.KEY_POSTFACH_ID, "123");
+			var formData =  FormData.builder().formData(createHeaders(AfmHeaderMapper.HEADER_FIELD, headers)).build();
+
+			var result = mapper.getHeaders(formData);
+
+			assertThat(result).isEqualTo(headers);
+		}
+
+		@Test
+		void shouldReturnEmptyMapWhenNoHeaders() {
+			var formData = FormData.builder().build();
+
+			var result = mapper.getHeaders(formData);
+
+			assertThat(result).isEmpty();
+		}
+	}
+
+	private Map<String, Object> createHeaders(String key, Object value) {
+		var map = new HashMap<String, Object>();
+		map.put(key, value);
+		return map;
+	}
+}
\ No newline at end of file
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapperTest.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapperTest.java
index 47e58c47132daae7675ac23a34c1134764a08cc9..e40b13db45356729cd68b0fec172931b999c4c17 100644
--- a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapperTest.java
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmAntragstellerMapperTest.java
@@ -24,6 +24,8 @@
 package de.ozgcloud.eingang.semantik.enginebased.afm;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 
 import java.util.HashMap;
 import java.util.UUID;
@@ -33,6 +35,9 @@ 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;
+import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import de.ozgcloud.eingang.common.formdata.Antragsteller;
 import de.ozgcloud.eingang.common.formdata.FormData;
@@ -40,7 +45,11 @@ import de.ozgcloud.eingang.semantik.enginebased.afm.AfmAntragstellerMapper;
 
 class AfmAntragstellerMapperTest {
 
-	private AfmAntragstellerMapper mapper = new AfmAntragstellerMapper();
+	@InjectMocks
+	private AfmAntragstellerMapper mapper;
+
+	@Mock
+	private AfmAntragstellerHeaderMapper antragstellerHeaderMapper;
 
 	private FormData formData = FormData.builder().formData(AfmAntragstellerTestFactory.createFormDataMap()).build();
 	
@@ -48,6 +57,17 @@ class AfmAntragstellerMapperTest {
 	@Nested
 	class TestParseFormData {
 
+		@Test
+		void shouldCallHeaderMapper() {
+			FormData expectedFormData = mock(FormData.class);
+			when(antragstellerHeaderMapper.isResponsible(any())).thenReturn(true);
+			when(antragstellerHeaderMapper.parseAntragstellerData(any())).thenReturn(expectedFormData);
+
+			var processedFormData = parseFormData(formData);
+
+			assertThat(processedFormData).isEqualTo(expectedFormData);
+		}
+
 		@Test
 		void shouldDoNothingOnNullAntragstaller() {
 			var formData = FormData.builder().formData(new HashMap<>()).build();
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmHeaderTestFactory.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmHeaderTestFactory.java
index d3705fa4277593df269478566b7f1f221848d59b..28e807688770910087b28d5ec2a228c4f90789dd 100644
--- a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmHeaderTestFactory.java
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/enginebased/afm/AfmHeaderTestFactory.java
@@ -38,6 +38,17 @@ public class AfmHeaderTestFactory {
 	public static final String FORM = "Kleiner Waffenschein gem. § 10 Abs. 4 Satz 4 Waffengesetz (WaffG)";
 	public static final String SENDER = "afm.schleswig-holstein.de";
 
+	public static final String CUSTOM_POSTFACH_ID = "postfach_id";
+	public static final String CUSTOM_VORNAME = "vorname";
+	public static final String CUSTOM_NACHNAME = "nachname";
+	public static final String CUSTOM_GEBURTSNAME = "Geburtsname";
+	public static final String CUSTOM_GEBURTSORT = "geburtsort";
+	public static final String CUSTOM_EMAIL = "email";
+	public static final String CUSTOM_TELEFON = "telefon";
+	public static final String CUSTOM_STRASSE = "strasse";
+	public static final String CUSTOM_PLZ = "plz";
+	public static final String CUSTOM_ORT = "ort";
+
 	public static final String POSTFACH_NAME_ID = "name-id-value";
 
 	public static final int REST_RESPONSE_NAME_MEMBER_SCOPE_MAILBOX_TYPE_VALUE = 1;
@@ -58,6 +69,22 @@ public class AfmHeaderTestFactory {
 		map.put(AfmHeaderMapper.FORM_ID, FORM_ID);
 		map.put(AfmHeaderMapper.FORM, FORM);
 		map.put(AfmHeaderMapper.SENDER, SENDER);
+		map.putAll(createCustomHeaderMap());
+		return map;
+	}
+
+	public static Map<String, Object> createCustomHeaderMap() {
+		var map = new HashMap<String, Object>();
+		map.put(AfmAntragstellerHeaderMapper.KEY_POSTFACH_ID, CUSTOM_POSTFACH_ID);
+		map.put(AfmAntragstellerHeaderMapper.KEY_VORNAME, CUSTOM_VORNAME);
+		map.put(AfmAntragstellerHeaderMapper.KEY_NACHNAME, CUSTOM_NACHNAME);
+		map.put(AfmAntragstellerHeaderMapper.KEY_GEBURTSNAME, CUSTOM_GEBURTSNAME);
+		map.put(AfmAntragstellerHeaderMapper.KEY_GEBURTSORT, CUSTOM_GEBURTSORT);
+		map.put(AfmAntragstellerHeaderMapper.KEY_EMAIL, CUSTOM_EMAIL);
+		map.put(AfmAntragstellerHeaderMapper.KEY_TELEFON, CUSTOM_TELEFON);
+		map.put(AfmAntragstellerHeaderMapper.KEY_STRASSE, CUSTOM_STRASSE);
+		map.put(AfmAntragstellerHeaderMapper.KEY_PLZ, CUSTOM_PLZ);
+		map.put(AfmAntragstellerHeaderMapper.KEY_ORT, CUSTOM_ORT);
 		return map;
 	}