diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java
index 4418a4fc7c3c59a0ee27516a4ad6f536a77bf22a..acb3f7384ec445591ad3da3f63f43aef9d6659f1 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/AntragstellerTestFactory.java
@@ -38,6 +38,9 @@ public class AntragstellerTestFactory {
 	public static final String SUB_VERBOTENE_VEREINIGUNG_KEY = "MitgliedschaftInVerboternerVereinigung";
 	public static final String SUB_VERBOTENE_VEREINIGUNG_VALUE = "true";
 
+	public static final Map<String, Object> DATA = Map.of(GEBIET_BEZEICHNUNG_KEY, GEBIET_BEZEICHNUNG_VALUE,
+			SUB_PERSOENLICHE_EIGNUNG, Map.of(SUB_VERBOTENE_VEREINIGUNG_KEY, SUB_VERBOTENE_VEREINIGUNG_VALUE));
+
 	public static final String VORNAME = "Theo";
 	public static final String NACHNAME = "Test";
 	public static final String GEBURTSNAME = "Toast";
@@ -82,8 +85,6 @@ public class AntragstellerTestFactory {
 				.plz(PLZ)
 				.ort(ORT)
 				.postfachId(POSTFACH_ID)
-				.data(Map.of(GEBIET_BEZEICHNUNG_KEY, GEBIET_BEZEICHNUNG_VALUE,
-						SUB_PERSOENLICHE_EIGNUNG,
-						Map.of(SUB_VERBOTENE_VEREINIGUNG_KEY, SUB_VERBOTENE_VEREINIGUNG_VALUE)));
+				.data(DATA);
 	}
 }
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/FormDataTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/FormDataTestFactory.java
index 1c5ceaddf1b27323ff33316cb7328533eb904a96..2dc3a7f665e7adc01738d8c7e7ff07ef4b919f3a 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/FormDataTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/FormDataTestFactory.java
@@ -28,6 +28,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 import de.ozgcloud.eingang.common.formdata.FormData.FormDataControl;
 import lombok.AccessLevel;
@@ -36,6 +37,9 @@ import lombok.NoArgsConstructor;
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class FormDataTestFactory {
 
+	public static final Antragsteller ANTRAGSTELLER = AntragstellerTestFactory.create();
+	public static final FormHeader HEADER = FormHeaderTestFactory.create();
+	public static final String ID = UUID.randomUUID().toString();
 	public static final String SIMPLE_VALUE_KEY = "kontaktsystemtypid";
 	public static final String SIMPLE_VALUE = "233034600";
 
@@ -54,12 +58,9 @@ public class FormDataTestFactory {
 	public static final Map<String, Object> NESTED_LIST_OBJECTS_ELEMENT_2 = Map.of("o3", "o4");
 	public static final List<Map<String, Object>> NESTED_LIST_WITH_OBJECTS = List.of(NESTED_LIST_OBJECTS_ELEMENT_1, NESTED_LIST_OBJECTS_ELEMENT_2);
 
-	public static final String ATTACHMENT_GROUP_2 = "FileGroup2";
-
 	public static final String VORGANG_ID = "vorgangId";
 
-	public static final List<IncomingFileGroup> ATTACHMENTS = List.of(IncomingFileGroupTestFactory.create(),
-			IncomingFileGroupTestFactory.createBuilder().name(ATTACHMENT_GROUP_2).build());
+	public static final List<IncomingFileGroup> ATTACHMENTS = List.of(IncomingFileGroupTestFactory.create());
 	public static final List<IncomingFile> REPRESENTATIONS = List.of(IncomingFileTestFactory.create());
 
 	public static final FormDataControl FORM_DATA_CONTROL = FormDataControlTestFactory.create();
@@ -76,17 +77,15 @@ public class FormDataTestFactory {
 
 	public static FormData.FormDataBuilder createBuilder() {
 		return FormData.builder()
-				.header(FormHeaderTestFactory.create())
-				.antragsteller(AntragstellerTestFactory.create())
+				.id(ID)
+				.header(HEADER)
+				.antragsteller(ANTRAGSTELLER)
 				.zustaendigeStelle(ZustaendigeStelleTestFactory.create())
 				.control(FORM_DATA_CONTROL)
 				.formData(FORM_DATA)
-				// TODO nach entfernen des zweiten Attachments den Wert auf 1 setzen
-				.numberOfAttachments(2)
-				// TODO zweites Attachment aus der TestFactory entfernen und die entsprechenden
-				// Tests anpassen
+				.numberOfAttachments(ATTACHMENTS.size())
 				.attachments(ATTACHMENTS)
-				.numberOfRepresentations(1)
+				.numberOfRepresentations(REPRESENTATIONS.size())
 				.representations(REPRESENTATIONS);
 	}
 
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileGroupTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileGroupTestFactory.java
index ebabd58ef35199c82391f7f4a07c19b35239dcfe..62d245773b2dbefda8ca421920a153fac9f2d2fb 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileGroupTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileGroupTestFactory.java
@@ -25,23 +25,12 @@ package de.ozgcloud.eingang.common.formdata;
 
 import java.util.List;
 
-public class IncomingFileGroupTestFactory {
-	public static final String XDOMEA_XML_NAME = "xdomea.xml";
-	public static final String REPR_XML_NAME = "repr.xml";
-	public static final String REPR_PDF_NAME = "repr.pdf";
-	public static final String ATTATCHMENT_XML_NAME = "att.xml";
-	public static final String ATTATCHMENT_PNG_NAME = "att.png";
-	public static final String ATTATCHMENT_PDF_NAME = "att.pdf";
-
-	public static final String INCOMING_FILE_ID = "xxx";
-	public static final String ID = "id";
-	public static final String FILE_REF1 = "FileRef1";
-	public static final String VENDOR_ID_XXX = "vendorId:xxx";
+import com.thedeanda.lorem.LoremIpsum;
 
-	public static final String NAME = "Ausweis";
+public class IncomingFileGroupTestFactory {
 
-	public static final IncomingFile INCOMING_FILE = IncomingFile.builder().id(INCOMING_FILE_ID).vendorId(VENDOR_ID_XXX).build();
-	public static final List<IncomingFileGroup> FILE_GROUPS = List.of(IncomingFileGroup.builder().files(List.of(INCOMING_FILE)).build());
+	public static final String NAME = LoremIpsum.getInstance().getWords(1);
+	public static final List<IncomingFile> INCOMING_FILES = List.of(IncomingFileTestFactory.create());
 
 	public static IncomingFileGroup create() {
 		return createBuilder().build();
@@ -50,7 +39,7 @@ public class IncomingFileGroupTestFactory {
 	public static IncomingFileGroup.IncomingFileGroupBuilder createBuilder() {
 		return IncomingFileGroup.builder()
 				.name(NAME)
-				.files(List.of(IncomingFileTestFactory.create()));
+				.files(INCOMING_FILES);
 	}
 
 }
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileTestFactory.java
index 03fd991276406c2bb0ef46a4ba54d22704d1c065..85a10843692f48f8707d24649087c9a8cad98009 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/IncomingFileTestFactory.java
@@ -23,6 +23,7 @@
  */
 package de.ozgcloud.eingang.common.formdata;
 
+import java.io.File;
 import java.util.UUID;
 
 import org.springframework.http.MediaType;
@@ -35,12 +36,13 @@ import lombok.SneakyThrows;
 public class IncomingFileTestFactory {
 
 	public static final String ID = UUID.randomUUID().toString();
-	public static final String VENDOR_ID = IncomingFileGroupTestFactory.VENDOR_ID_XXX;
+	public static final String VENDOR_ID = "vendorId:xxx";
 	public static final String NAME = "XML-Daten.xml";
 	public static final String CONTENT_TYPE = "application/xml";
 	public static final String PDF_CONTENT_TYPE = MediaType.APPLICATION_PDF_VALUE;
 	public static final String JSON_CONTENT_TYPE = MediaType.APPLICATION_JSON_VALUE;
 	public static final byte[] CONTENT = "TESTCONTENT1".getBytes();
+	public static final File FILE = TempFileUtils.writeTmpFile(CONTENT);
 	public static final long SIZE = 12;
 
 	public static IncomingFile create() {
@@ -53,7 +55,7 @@ public class IncomingFileTestFactory {
 				.vendorId(VENDOR_ID)
 				.name(NAME)
 				.contentType(CONTENT_TYPE)
-				.file(TempFileUtils.writeTmpFile(CONTENT))
+				.file(FILE)
 				.size(SIZE);
 	}
 
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/PostfachAddressTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/PostfachAddressTestFactory.java
index 0e3e2a2816168331b48355740403339c9ffe72ec..a7d2fa3ceaa6c4dc68888be3ec6f5450135b22e4 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/PostfachAddressTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/PostfachAddressTestFactory.java
@@ -25,11 +25,13 @@ package de.ozgcloud.eingang.common.formdata;
 
 import java.util.UUID;
 
+import org.apache.commons.lang3.RandomUtils;
+
 import de.ozgcloud.eingang.common.formdata.ServiceKonto.PostfachAddress;
 
 public class PostfachAddressTestFactory {
 
-	public static final int POSTFACH_ADDRESS_TYPE = 1;
+	public static final int POSTFACH_ADDRESS_TYPE = RandomUtils.insecure().randomInt();
 	public static final String VERSION = "1.0";
 	public static final String POSTFACH_ID = UUID.randomUUID().toString();
 
diff --git a/common/src/test/java/de/ozgcloud/eingang/common/formdata/ZustaendigeStelleTestFactory.java b/common/src/test/java/de/ozgcloud/eingang/common/formdata/ZustaendigeStelleTestFactory.java
index 5b14fe2d568bb8af1acdd96dbbf2972fac3aa101..34adca8f00a4b850f5b0907ded56566c58d34056 100644
--- a/common/src/test/java/de/ozgcloud/eingang/common/formdata/ZustaendigeStelleTestFactory.java
+++ b/common/src/test/java/de/ozgcloud/eingang/common/formdata/ZustaendigeStelleTestFactory.java
@@ -23,6 +23,8 @@
  */
 package de.ozgcloud.eingang.common.formdata;
 
+import com.thedeanda.lorem.LoremIpsum;
+
 public class ZustaendigeStelleTestFactory {
 
 	public static final String ORGANISATIONSEINHEIT_ID = "08150815";
@@ -33,6 +35,7 @@ public class ZustaendigeStelleTestFactory {
 	public static final String HAUSANSCHRIFT_PLZ = "12345";
 	public static final String HAUSANSCHRIFT_ORT = "Musterort";
 	public static final String TELEFON = "0123456789";
+	public static final String BEZEICHNUNG = LoremIpsum.getInstance().getWords(5);
 
 	public static ZustaendigeStelle create() {
 		return createBuilder().build();
@@ -42,6 +45,7 @@ public class ZustaendigeStelleTestFactory {
 		return ZustaendigeStelle.builder() //
 				.organisationseinheitenId(ORGANISATIONSEINHEIT_ID)
 				.email(EMAIL)
+				.bezeichnung(BEZEICHNUNG)
 				.gemeindeSchluessel(GEMEINDE_SCHLUESSEL)
 				.amtlicherRegionalSchluessel(AMTLICHER_REGIONAL_SCHLUESSEL)
 				.hausanschriftStrasse(HAUSANSCHRIFT_STRASSE)
diff --git a/eingang-manager-interface/pom.xml b/eingang-manager-interface/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b877903e6c7055ba30c1a7009f970a992694fe60
--- /dev/null
+++ b/eingang-manager-interface/pom.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (C) 2025 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>de.ozgcloud.common</groupId>
+		<artifactId>ozgcloud-common-dependencies</artifactId>
+		<version>4.11.0</version>
+		<relativePath />
+	</parent>
+
+	<groupId>de.ozgcloud.eingang</groupId>
+	<artifactId>eingang-manager-interface</artifactId>
+	<version>2.19.0-SNAPSHOT</version>
+
+	<name>OZG-Cloud Eingang Manager gRPC Interface</name>
+	<description>Interface (gRPC) for Eingang Manager Server</description>
+	<inceptionYear>2025</inceptionYear>
+
+	<properties>
+		<vorgang-manager.version>2.23.0</vorgang-manager.version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+		<java.version>21</java.version>
+		<maven.compiler.source>${java.version}</maven.compiler.source>
+		<maven.compiler.target>${java.version}</maven.compiler.target>
+
+		<find-and-replace-maven-plugin.version>1.1.0</find-and-replace-maven-plugin.version>
+	</properties>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>de.ozgcloud.common</groupId>
+				<artifactId>ozgcloud-common-dependencies</artifactId>
+				<version>${ozgcloud-common.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<dependencies>
+		<!-- OZG-Cloud -->
+		<dependency>
+			<groupId>de.ozgcloud.vorgang</groupId>
+			<artifactId>vorgang-manager-interface</artifactId>
+			<version>${vorgang-manager.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>de.ozgcloud.vorgang</groupId>
+			<artifactId>vorgang-manager-interface</artifactId>
+			<classifier>sources</classifier>
+			<scope>provided</scope>
+			<version>${vorgang-manager.version}</version>
+		</dependency>
+		<!-- GRPC -->
+		<dependency>
+			<groupId>io.grpc</groupId>
+			<artifactId>grpc-stub</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.grpc</groupId>
+			<artifactId>grpc-protobuf</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>jakarta.annotation</groupId>
+			<artifactId>jakarta.annotation-api</artifactId>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<sourceDirectory>src/main/protobuf</sourceDirectory>
+
+		<extensions>
+			<extension>
+				<groupId>kr.motd.maven</groupId>
+				<artifactId>os-maven-plugin</artifactId>
+			</extension>
+		</extensions>
+
+		<plugins>
+			<plugin>
+				<groupId>com.github.os72</groupId>
+				<artifactId>protoc-jar-maven-plugin</artifactId>
+				<version>${protoc-jar-plugin.version}</version>
+				<executions>
+					<execution>
+						<phase>generate-sources</phase>
+						<goals>
+							<goal>run</goal>
+						</goals>
+						<configuration>
+							<protocVersion>${protobuf.version}</protocVersion>
+							<includeMavenTypes>direct</includeMavenTypes>
+							<outputTargets>
+								<outputTarget>
+									<type>java</type>
+								</outputTarget>
+								<outputTarget>
+									<type>grpc-java</type>
+									<pluginArtifact>
+										io.grpc:protoc-gen-grpc-java:${protoc-gen.version}</pluginArtifact>
+								</outputTarget>
+							</outputTargets>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>io.github.floverfelt</groupId>
+				<artifactId>find-and-replace-maven-plugin</artifactId>
+				<version>${find-and-replace-maven-plugin.version}</version>
+				<executions>
+					<execution>
+						<id>exec</id>
+						<phase>process-sources</phase>
+						<goals>
+							<goal>find-and-replace</goal>
+						</goals>
+						<configuration>
+							<replacementType>file-contents</replacementType>
+							<baseDir>target/generated-sources/</baseDir>
+							<findRegex>javax</findRegex>
+							<replaceValue>jakarta</replaceValue>
+							<recursive>true</recursive>
+							<fileMask>.java</fileMask>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>3.2.1</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+	<distributionManagement>
+		<repository>
+			<id>ozg-nexus</id>
+			<name>ozg-releases</name>
+			<url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
+		</repository>
+		<snapshotRepository>
+			<id>ozg-snapshots-nexus</id>
+			<name>ozg-snapshots</name>
+			<url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
+		</snapshotRepository>
+	</distributionManagement>
+</project>
\ No newline at end of file
diff --git a/eingang-manager-interface/src/main/protobuf/forward.model.proto b/eingang-manager-interface/src/main/protobuf/forward.model.proto
new file mode 100644
index 0000000000000000000000000000000000000000..29076e289280f43cb1693eefe6b74696404dea74
--- /dev/null
+++ b/eingang-manager-interface/src/main/protobuf/forward.model.proto
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
+ * Ministerpräsidenten des Landes Schleswig-Holstein
+ * Staatskanzlei
+ * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
+ *
+ * Lizenziert unter der EUPL, Version 1.2 oder - sobald
+ * diese von der Europäischen Kommission genehmigt wurden -
+ * Folgeversionen der EUPL ("Lizenz");
+ * Sie dürfen dieses Werk ausschließlich gemäß
+ * dieser Lizenz nutzen.
+ * Eine Kopie der Lizenz finden Sie hier:
+ *
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Sofern nicht durch anwendbare Rechtsvorschriften
+ * gefordert oder in schriftlicher Form vereinbart, wird
+ * die unter der Lizenz verbreitete Software "so wie sie
+ * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
+ * ausdrücklich oder stillschweigend - verbreitet.
+ * Die sprachspezifischen Genehmigungen und Beschränkungen
+ * unter der Lizenz sind dem Lizenztext zu entnehmen.
+ */
+syntax = "proto3";
+
+package de.ozgcloud.eingang.forwarder;
+
+import "vorgang.model.proto";
+
+option java_multiple_files = true;
+option java_package = "de.ozgcloud.eingang.forwarding";
+option java_outer_classname = "RouteForwardingModelProto";
+
+
+message GrpcRouteForwardingRequest {
+  oneof request {
+    GrpcRouteForwarding routeForwarding = 1;
+    GrpcAttachment attachment = 2;
+    GrpcRepresentation representation = 3;
+  }
+}
+
+message GrpcRouteForwardingResponse {
+
+}
+
+message GrpcRouteForwarding {
+  GrpcEingangStub eingangStub = 1;
+  GrpcRouteCriteria routeCriteria = 2;
+}
+
+message GrpcEingangStub {
+  de.ozgcloud.vorgang.vorgang.GrpcEingangHeader header = 1;
+  de.ozgcloud.vorgang.vorgang.GrpcAntragsteller antragsteller = 2;
+  de.ozgcloud.vorgang.vorgang.GrpcFormData formData = 3;
+}
+
+message GrpcRouteCriteria {
+  string organisationEinheitId = 1;
+}
+
+message GrpcAttachment {
+  oneof attachment {
+    GrpcAttachmentFile file = 1;
+    GrpcFileContent content = 2;
+  }
+}
+
+message GrpcAttachmentFile {
+  string groupName = 1;
+  string fileName = 2;
+  string contentType = 3;
+  string vendorId = 4;
+  int64 size = 5;
+}
+
+message GrpcRepresentation {
+  oneof representation {
+    GrpcRepresentationFile file = 1;
+    GrpcFileContent content = 2;
+  }
+}
+
+message GrpcRepresentationFile {
+  string fileName = 1;
+  string contentType = 2;
+  string vendorId = 3;
+  int64 size = 4;
+}
+
+message GrpcFileContent {
+  bytes content = 1;
+  bool isEndOfFile = 2;
+}
\ No newline at end of file
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteria.java b/eingang-manager-interface/src/main/protobuf/forward.proto
similarity index 70%
rename from forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteria.java
rename to eingang-manager-interface/src/main/protobuf/forward.proto
index b52e416dd52104bc590d4e2f309fdc44660a93bb..ef695a3e83718e9316070406ea00d37e566a42f8 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteria.java
+++ b/eingang-manager-interface/src/main/protobuf/forward.proto
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,18 +21,19 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+syntax = "proto3";
+
 package de.ozgcloud.eingang.forwarder;
 
-import java.util.Optional;
+import "forward.model.proto";
+
+option java_multiple_files = true;
+option java_package = "de.ozgcloud.eingang.forwarder";
+option java_outer_classname = "RouteForwardingProto";
 
-import lombok.Builder;
-import lombok.Getter;
+service RouteForwardingService {
 
-@Getter
-@Builder
-public class RouteCriteria {
+  rpc RouteForwarding(stream GrpcRouteForwardingRequest) returns (GrpcRouteForwardingResponse) {
+  }
 
-	private Optional<String> gemeindeSchluessel;
-	private Optional<String> webserviceUrl;
-	private Optional<String> organisationseinheitenId;
-}
+}
\ No newline at end of file
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/AntragstellerMapper.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/AntragstellerMapper.java
index 0c65466d79e1fdb5f0f5e5472fbd2bed4b013536..e7b648eee846205964c148f365b83d6b58fe25fd 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/AntragstellerMapper.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/AntragstellerMapper.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.Map;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDataUtil.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDataUtil.java
index 6d527356735d785b8f113da8d22473e8d200611c..b3dea53e5c1e0628909b7d852db8d3b8691b3c55 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDataUtil.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDataUtil.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.List;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDocumentReader.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDocumentReader.java
index de7e3961ccb4ae9b99eedf79c8c25beccc4d4abb..8301f01eaf84d0c1cf3280cf81d3f9e83d7c46ba 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDocumentReader.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimDocumentReader.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.Collections;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimSchemeHelper.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimSchemeHelper.java
index 054589340915925e6aa49ca9da20c88858eb377b..9fefeba4d8e90153bb214b2ffe78a292d00b77d1 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimSchemeHelper.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/FimSchemeHelper.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.List;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/HeaderMapper.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/HeaderMapper.java
index 63befb6637b368805c57cfe354df8d4974ad74ba..632636410382b8c6f230d6ef962d1e8efd7894a6 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/HeaderMapper.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/HeaderMapper.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.List;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/errorhandling/FimException.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/errorhandling/FimException.java
index 68be9eb443298ed45cafcbe646f73b85de9c6519..c7333daf4d31c4fac797a0532f27ef551f3bafa4 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/errorhandling/FimException.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/errorhandling/FimException.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim.common.errorhandling;
 
 import de.ozgcloud.eingang.common.errorhandling.TechnicalException;
diff --git a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/xml/DocumentHelper.java b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/xml/DocumentHelper.java
index 2368cd4ddd96ed23fe11e3c8553391d78fd19e22..966b2cc08c3ca86bde05d2c797990b4fccbc48f3 100644
--- a/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/xml/DocumentHelper.java
+++ b/fim-adapter/src/main/java/de/ozgcloud/eingang/fim/common/xml/DocumentHelper.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim.common.xml;
 
 import java.io.File;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerMapperTest.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerMapperTest.java
index f6daa78ea5d7a00b56f86ed99941c87e1e0d6cc7..8dd4950ece666f960e734af705df74aa2efd48b5 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerMapperTest.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerMapperTest.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerTestFactory.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerTestFactory.java
index e72806960894dce1f415e28f1624369c96852c71..6e14ba107049d8ecb2743524c05db215079c40a1 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerTestFactory.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/AntragstellerTestFactory.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.util.HashMap;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimBasedAdapterITCase.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimBasedAdapterITCase.java
index e32906bc408d40d53b0fc8969f6aa0fa5fd63bf3..ac669d63c5ae513bee4f8aadd44e7fdddcd8dd9c 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimBasedAdapterITCase.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimBasedAdapterITCase.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDataUtilTest.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDataUtilTest.java
index cb48785bcad73409f81fb3606a8f4e260b745445..631029fe3284289e82752e62d7c3ffe537856674 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDataUtilTest.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDataUtilTest.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDocumentTestHelper.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDocumentTestHelper.java
index 610263cc895893fd2abe162e469b405df679b7da..e5bbe51daf0c4f9b39f91fe35ee117496b1c50fd 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDocumentTestHelper.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimDocumentTestHelper.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import java.io.File;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimSchemeHelperTest.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimSchemeHelperTest.java
index 7553fefb39eecae195f94a9fa79706efad3d5d52..d768484446fa499efa389c4caf58ea40043a9a66 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimSchemeHelperTest.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimSchemeHelperTest.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimServiceTest.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimServiceTest.java
index 4d090842c766264a4886e3251acf72358d0d6152..c9ff9d8de6153bae94ca16bfbcc59f2d5ab74d4e 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimServiceTest.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/FimServiceTest.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/HeaderMapperTest.java b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/HeaderMapperTest.java
index eef87830a7eaa9b3760d037b07cb9d24dc692315..b552254518b9c70f218f34a227595cfa42e895b8 100644
--- a/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/HeaderMapperTest.java
+++ b/fim-adapter/src/test/java/de/ozgcloud/eingang/fim/HeaderMapperTest.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.fim;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormCycleFormDataTestFactory.java b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormCycleFormDataTestFactory.java
index 066b77ac5bcafacce46dab4070ea6f9bc52a7630..edff3cd6ccbf0a7863bfaca5fa8dbea645ea59c6 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormCycleFormDataTestFactory.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormCycleFormDataTestFactory.java
@@ -24,6 +24,7 @@
 package de.ozgcloud.eingang.formcycle;
 
 import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
 import de.ozgcloud.eingang.formcycle.FormCycleFormData.Builder;
 import de.ozgcloud.vorgang.common.grpc.GrpcFormDataTestFactory;
 import de.ozgcloud.vorgang.vorgang.GrpcFormData;
@@ -43,7 +44,7 @@ public class FormCycleFormDataTestFactory {
 				.setFormData(GrpcFormDataTestFactory.create())
 				.addAttachmentGroup(FormCycleAttachmentGroup.newBuilder()
 						.setName(IncomingFileGroupTestFactory.NAME)
-						.addFileId(IncomingFileGroupTestFactory.VENDOR_ID_XXX)
+						.addFileId(IncomingFileTestFactory.VENDOR_ID)
 						.build());
 	}
 
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerITCase.java b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerITCase.java
index f63d7df6efbe2c562f896b60e7d1981fdd32e726..61daa9f4a91a7690dd119bd7c2819553d8f00435 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerITCase.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerITCase.java
@@ -37,7 +37,6 @@ import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 
 import de.ozgcloud.common.test.ITCase;
-import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
 import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
 import de.ozgcloud.eingang.semantik.SemantikAdapter;
 import de.ozgcloud.vorgang.common.grpc.GrpcFormDataTestFactory;
@@ -46,11 +45,11 @@ import de.ozgcloud.vorgang.vorgang.GrpcSubForm;
 import lombok.SneakyThrows;
 
 @ITCase
-//@SpringBootTest(properties = {
-//		"grpc.client.vorgang-manager-local.address=static://127.0.0.1:9090",
-//		"grpc.client.vorgang-manager-local.negotiationType=PLAINTEXT"
-//})
-//@ActiveProfiles("itcase")
+// @SpringBootTest(properties = {
+// "grpc.client.vorgang-manager-local.address=static://127.0.0.1:9090",
+// "grpc.client.vorgang-manager-local.negotiationType=PLAINTEXT"
+// })
+// @ActiveProfiles("itcase")
 @AutoConfigureMockMvc
 class FormDataControllerITCase {
 
@@ -76,7 +75,7 @@ class FormDataControllerITCase {
 							.file(new MockMultipartFile("formData", null, FormDataController.HTTP_TYPE_PROTOBUF, buildTestFormData()))
 							.file(IncomingFileTestFactory.asMultipartFile("representations"))
 							.file(asMultipartFile("attachments",
-									createBuilder().name(IncomingFileGroupTestFactory.VENDOR_ID_XXX + "__" + NAME).build())));
+									createBuilder().name(IncomingFileTestFactory.VENDOR_ID + "__" + NAME).build())));
 		}
 	}
 
diff --git a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerTest.java b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerTest.java
index af16a675df3161341c8fcec51aec4f96a16f4607..e9fdd387fcc97a176170c63cf40e392e7fa1b3f4 100644
--- a/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerTest.java
+++ b/formcycle-adapter/formcycle-adapter-impl/src/test/java/de/ozgcloud/eingang/formcycle/FormDataControllerTest.java
@@ -239,7 +239,7 @@ class FormDataControllerTest {
 							.file(new MockMultipartFile("formData", null, FormDataController.HTTP_TYPE_PROTOBUF, buildTestFormData()))
 							.file(IncomingFileTestFactory.asMultipartFile("representations"))
 							.file(asMultipartFile("attachments",
-									createBuilder().name(IncomingFileGroupTestFactory.VENDOR_ID_XXX).build())));
+									createBuilder().name(IncomingFileTestFactory.VENDOR_ID).build())));
 		}
 	}
 
diff --git a/formcycle-adapter/pom.xml b/formcycle-adapter/pom.xml
index 2e7f85ed29e3331585bbcf5adaa8ab7b0972b3d0..04849487dd004df43e50d441f61f2ef539c63b52 100644
--- a/formcycle-adapter/pom.xml
+++ b/formcycle-adapter/pom.xml
@@ -63,6 +63,10 @@
 			<groupId>de.ozgcloud.vorgang</groupId>
 			<artifactId>vorgang-manager-interface</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>de.ozgcloud.eingang</groupId>
+			<artifactId>forwarder</artifactId>
+		</dependency>
 		<dependency>
 			<groupId>de.ozgcloud.vorgang</groupId>
 			<artifactId>vorgang-manager-interface</artifactId>
diff --git a/forwarder/pom.xml b/forwarder/pom.xml
index 8e11aac68cb31e2265202b09a8cef1a841d69293..4fabecd6af1c23ce9d991e6c1db97a49c706da87 100644
--- a/forwarder/pom.xml
+++ b/forwarder/pom.xml
@@ -49,6 +49,10 @@
 			<groupId>de.ozgcloud.eingang</groupId>
 			<artifactId>common</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>de.ozgcloud.eingang</groupId>
+			<artifactId>eingang-manager-interface</artifactId>
+		</dependency>
 		<dependency>
 			<groupId>de.ozgcloud.eingang</groupId>
 			<artifactId>router</artifactId>
@@ -86,6 +90,25 @@
 			<artifactId>lombok</artifactId>
 		</dependency>
 
+		<!-- Test -->
+		<dependency>
+			<groupId>de.ozgcloud.eingang</groupId>
+			<artifactId>common</artifactId>
+			<type>test-jar</type>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>de.ozgcloud.eingang</groupId>
+			<artifactId>router</artifactId>
+			<type>test-jar</type>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>de.ozgcloud.vorgang</groupId>
+			<artifactId>vorgang-manager-utils</artifactId>
+			<type>test-jar</type>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 
 	<build>
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/ContentCollector.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/ContentCollector.java
new file mode 100644
index 0000000000000000000000000000000000000000..5099f2385c580c3651e302d509ed9e43cb5defd8
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/ContentCollector.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+import org.apache.commons.io.IOUtils;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
+import lombok.Builder;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+class ContentCollector {
+	private static final int CHUNK_SIZE = 1024 * 64;
+	private static final long TIMEOUT_MINUTES = 10;
+
+	private final IncomingFile incomingFile;
+
+	private final PipedOutputStream pipedOutput;
+	private final PipedInputStream pipedInput;
+	private final CompletableFuture<File> receivingFileContent;
+
+	@Builder
+	private ContentCollector(Function<InputStream, CompletableFuture<File>> fileSaver, IncomingFile incomingFile) {
+		this.incomingFile = incomingFile;
+		try {
+			pipedInput = new PipedInputStream(CHUNK_SIZE);
+			pipedOutput = new PipedOutputStream(pipedInput);
+			receivingFileContent = fileSaver.apply(pipedInput);
+		} catch (IOException e) {
+			throw new TechnicalException("Upload initialization failed", e);
+		}
+	}
+
+	public Optional<IncomingFile> collect(GrpcFileContent fileContent) {
+		if (fileContent.getIsEndOfFile()) {
+			return Optional.of(handleEndOfFile());
+		}
+		try {
+			pipedOutput.write(fileContent.getContent().toByteArray());
+		} catch (IOException e) {
+			throw new TechnicalException("Error when writing file content.", e);
+		}
+		return Optional.empty();
+	}
+
+	IncomingFile handleEndOfFile() {
+		closeOutputPipe();
+		return incomingFile.toBuilder().file(getSavedFileContent()).build();
+	}
+
+	File getSavedFileContent() {
+		try {
+			return receivingFileContent.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
+		} catch (ExecutionException | TimeoutException e) {
+			throw new TechnicalException("Receiving file failed.", e);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			throw new TechnicalException("Upload was interrupted.", e);
+		} finally {
+			closeInputPipe();
+		}
+	}
+
+	public void close() {
+		closeOutputPipe();
+		closeInputPipe();
+	}
+
+	void closeOutputPipe() {
+		IOUtils.closeQuietly(pipedOutput, e -> LOG.error("Cannot close output stream.", e));
+	}
+
+	void closeInputPipe() {
+		IOUtils.closeQuietly(pipedInput, e -> LOG.error("Cannot close input stream.", e));
+	}
+
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserver.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserver.java
new file mode 100644
index 0000000000000000000000000000000000000000..40dbc1b1a0adec125848dd949d3da0f1ca3eaf33
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserver.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentation;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
+import io.grpc.stub.StreamObserver;
+import lombok.Builder;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+class EingangStubReceiverStreamObserver implements StreamObserver<GrpcRouteForwardingRequest> {
+
+	private final RouteForwardingMapper routeForwardingMapper;
+	private final IncomingFileMapper incomingFileMapper;
+	private final IncomingFileGroupMapper incomingFileGroupMapper;
+	private final Function<InputStream, CompletableFuture<File>> fileSaver;
+	private final Consumer<FormData> formDataConsumer;
+	private final Consumer<GrpcRouteForwardingResponse> responseConsumer;
+
+	@Builder
+	public EingangStubReceiverStreamObserver(RouteForwardingMapper routeForwardingMapper, IncomingFileMapper incomingFileMapper,
+			IncomingFileGroupMapper incomingFileGroupMapper, Function<InputStream, CompletableFuture<File>> fileSaver,
+			Consumer<FormData> formDataConsumer, Consumer<GrpcRouteForwardingResponse> responseConsumer) {
+		this.routeForwardingMapper = routeForwardingMapper;
+		this.incomingFileMapper = incomingFileMapper;
+		this.incomingFileGroupMapper = incomingFileGroupMapper;
+		this.fileSaver = fileSaver;
+		this.formDataConsumer = formDataConsumer;
+		this.responseConsumer = responseConsumer;
+	}
+
+	private FormData formData;
+	private final List<IncomingFile> representations = new ArrayList<>();
+	private final Map<String, List<IncomingFile>> attachments = new HashMap<>();
+
+	private String groupName;
+	private ContentCollector contentCollector;
+
+	@Override
+	public synchronized void onNext(GrpcRouteForwardingRequest request) {
+		if (request.hasRouteForwarding()) {
+			handleRouteForwarding(request.getRouteForwarding());
+		}
+		if (request.hasAttachment()) {
+			handleAttachment(request.getAttachment());
+		}
+		if (request.hasRepresentation()) {
+			handleRepresentation(request.getRepresentation());
+		}
+	}
+
+	void handleRouteForwarding(GrpcRouteForwarding routeForwarding) {
+		if (Objects.nonNull(formData)) {
+			throw new IllegalStateException("Received second RouteForwarding. Send only one per request.");
+		}
+		formData = routeForwardingMapper.toFormData(routeForwarding);
+	}
+
+	void handleAttachment(GrpcAttachment attachment) {
+		if (attachment.hasFile()) {
+			verifyNoOngoingFileDownload();
+			contentCollector = buildContentCollector(incomingFileMapper.fromGrpcAttachmentFile(attachment.getFile()));
+			groupName = attachment.getFile().getGroupName();
+		} else {
+			verifyMetadataWasSent();
+			contentCollector.collect(attachment.getContent()).ifPresent(this::addAsAttachment);
+		}
+	}
+
+	void handleRepresentation(GrpcRepresentation representation) {
+		if (representation.hasFile()) {
+			verifyNoOngoingFileDownload();
+			contentCollector = buildContentCollector(incomingFileMapper.fromGrpcRepresentationFile(representation.getFile()));
+		} else {
+			verifyMetadataWasSent();
+			contentCollector.collect(representation.getContent()).ifPresent(this::addAsRepresentation);
+		}
+	}
+
+	void verifyNoOngoingFileDownload() {
+		if (Objects.nonNull(contentCollector)) {
+			throw new IllegalStateException("File metadata received before previous file content was completed.");
+		}
+	}
+
+	private ContentCollector buildContentCollector(IncomingFile incomingFile) {
+		return ContentCollector.builder()
+				.fileSaver(fileSaver)
+				.incomingFile(incomingFile)
+				.build();
+	}
+
+	void verifyMetadataWasSent() {
+		if (Objects.isNull(contentCollector)) {
+			throw new IllegalStateException("File content received before metadata.");
+		}
+	}
+
+	void addAsAttachment(IncomingFile completedIncomingFile) {
+		attachments.computeIfAbsent(groupName, s -> new ArrayList<>()).add(completedIncomingFile);
+		contentCollector = null;
+	}
+
+	void addAsRepresentation(IncomingFile completedIncomingFile) {
+		representations.add(completedIncomingFile);
+		contentCollector = null;
+	}
+
+	@Override
+	public synchronized void onError(Throwable t) {
+		LOG.error("Error happened. Receiving stream closed.", t);
+		contentCollector.close();
+	}
+
+	@Override
+	public synchronized void onCompleted() {
+		formDataConsumer.accept(assembleFormData());
+		responseConsumer.accept(GrpcRouteForwardingResponse.getDefaultInstance());
+	}
+
+	FormData assembleFormData() {
+		if (Objects.isNull(formData)) {
+			throw new IllegalStateException("Never received RouteForwarding containing EingangStub and RouteCriteria.");
+		}
+		return formData.toBuilder()
+				.representations(representations)
+				.attachments(attachments.entrySet().stream().map(incomingFileGroupMapper::fromMapEntry).toList())
+				.numberOfAttachments(attachments.size())
+				.numberOfRepresentations(representations.size())
+				.build();
+	}
+
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..91dcc0d3ea23794565de280ac8bfd192dd2401db
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.common.binaryfile.TempFileUtils;
+
+@Service
+class FileService {
+
+	@Async
+	public CompletableFuture<File> saveToFile(InputStream inputStream) {
+		return CompletableFuture.completedFuture(TempFileUtils.writeTmpFile(inputStream));
+	}
+
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteriaMapper.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapper.java
similarity index 67%
rename from forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteriaMapper.java
rename to forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapper.java
index d82fbd76b46f70c91d4fb175a0c69d42e2b2fb9d..ba1939b216c851f0af1f1b74f47248f351fcad7b 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteCriteriaMapper.java
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -23,19 +23,21 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
-import java.util.Optional;
+import java.util.List;
+import java.util.Map;
 
-import org.apache.commons.lang3.StringUtils;
 import org.mapstruct.Mapper;
 
-import de.ozgcloud.eingang.forwarding.GrpcRouteCriteria;
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroup;
 
 @Mapper
-interface RouteCriteriaMapper {
+public interface IncomingFileGroupMapper {
 
-	RouteCriteria fromGrpc(GrpcRouteCriteria grpcRouteCriteria);
-
-	default Optional<String> wrapWithOptional(String val) {
-		return Optional.ofNullable(StringUtils.trimToNull(val));
+	default IncomingFileGroup fromMapEntry(Map.Entry<String, List<IncomingFile>> entry) {
+		return IncomingFileGroup.builder()
+				.name(entry.getKey())
+				.files(entry.getValue())
+				.build();
 	}
 }
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileMapper.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..40176db7151e5c00e974300dbcb1b36bff0a74da
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/IncomingFileMapper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentationFile;
+
+@Mapper
+interface IncomingFileMapper {
+
+	@Mapping(target = "id", ignore = true)
+	@Mapping(target = "file", ignore = true)
+	@Mapping(target = "name", source = "fileName")
+	IncomingFile fromGrpcRepresentationFile(GrpcRepresentationFile representationFile);
+
+	@Mapping(target = "id", ignore = true)
+	@Mapping(target = "file", ignore = true)
+	@Mapping(target = "name", source = "fileName")
+	IncomingFile fromGrpcAttachmentFile(GrpcAttachmentFile representationFile);
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
index fa9c9d99a6ccb8c303eba891c60770b89daf35ce..322e7361ff4f3cc58808e60e14cd3103eab90804 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
@@ -23,27 +23,45 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
-import org.springframework.beans.factory.annotation.Autowired;
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
 
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
-import de.ozgcloud.eingang.router.GrpcEingangMapper;
 import io.grpc.stub.StreamObserver;
+import lombok.RequiredArgsConstructor;
 import net.devh.boot.grpc.server.service.GrpcService;
 
 @GrpcService
+@RequiredArgsConstructor
 public class RouteForwardingGrpcService extends RouteForwardingServiceGrpc.RouteForwardingServiceImplBase {
 
-	@Autowired
-	private RouteForwardingService service;
-	@Autowired
-	private RouteCriteriaMapper criteriaMapper;
-
-	@Autowired
-	private GrpcEingangMapper eingangMapper;
+	private final RouteForwardingMapper routeForwardingMapper;
+	private final IncomingFileMapper incomingFileMapper;
+	private final IncomingFileGroupMapper incomingFileGroupMapper;
+	private final RouteForwardingService routeForwardingService;
+	private final FileService fileService;
 
 	@Override
-	public void routeForwarding(GrpcRouteForwardingRequest request, StreamObserver<GrpcRouteForwardingResponse> responseObserver) {
-		service.route(criteriaMapper.fromGrpc(request.getRouteCriteria()), eingangMapper.toFormData(request.getEingang()));
+	public StreamObserver<GrpcRouteForwardingRequest> routeForwarding(StreamObserver<GrpcRouteForwardingResponse> responseObserver) {
+		return EingangStubReceiverStreamObserver.builder()
+				.fileSaver(this::saveFile)
+				.routeForwardingMapper(routeForwardingMapper)
+				.incomingFileMapper(incomingFileMapper)
+				.incomingFileGroupMapper(incomingFileGroupMapper)
+				.formDataConsumer(routeForwardingService::route)
+				.responseConsumer(repsonse -> respondWith(responseObserver, repsonse))
+				.build();
+	}
+
+	public CompletableFuture<File> saveFile(InputStream inputStream) {
+		return fileService.saveToFile(inputStream);
 	}
+
+	public void respondWith(StreamObserver<GrpcRouteForwardingResponse> responseObserver, GrpcRouteForwardingResponse response) {
+		responseObserver.onNext(response);
+		responseObserver.onCompleted();
+	}
+
 }
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapper.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3665e7f6c4ec8e65f721796aefcd2022101c148c
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+
+import de.ozgcloud.eingang.common.formdata.Antragsteller;
+import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.ZustaendigeStelle;
+import de.ozgcloud.eingang.forwarding.GrpcRouteCriteria;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
+import de.ozgcloud.eingang.router.ServiceKontoMapper;
+import de.ozgcloud.vorgang.common.grpc.GrpcFormDataMapper;
+import de.ozgcloud.vorgang.vorgang.GrpcAntragsteller;
+
+@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, //
+		nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, //
+		collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, //
+		uses = { ServiceKontoMapper.class, GrpcFormDataMapper.class })
+public interface RouteForwardingMapper {
+
+	@Mapping(target = "id", ignore = true)
+	@Mapping(target = "attachment", ignore = true)
+	@Mapping(target = "attachments", ignore = true)
+	@Mapping(target = "numberOfAttachments", ignore = true)
+	@Mapping(target = "representation", ignore = true)
+	@Mapping(target = "representations", ignore = true)
+	@Mapping(target = "numberOfRepresentations", ignore = true)
+	@Mapping(target = "control", ignore = true)
+	@Mapping(target = "zustaendigeStelles", ignore = true)
+	@Mapping(target = ".", source = "eingangStub")
+	@Mapping(target = "zustaendigeStelle", source = "routeCriteria")
+	FormData toFormData(GrpcRouteForwarding routeForwarding);
+
+	@Mapping(target = "data", source = "otherData")
+	Antragsteller mapAntragstellerFromGrpc(GrpcAntragsteller antragsteller);
+
+	default ZustaendigeStelle toZustaendigeStelle(GrpcRouteCriteria routeCriteria) {
+		return ZustaendigeStelle.builder().organisationseinheitenId(routeCriteria.getOrganisationEinheitId()).build();
+	}
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
index 0079be1be32bbe6c4d23ba12f491acb126840c9f..40e9b7707c89c642ec0a56b6f45aca9299aa6d3a 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
@@ -26,11 +26,16 @@ package de.ozgcloud.eingang.forwarder;
 import org.springframework.stereotype.Service;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.router.VorgangService;
+import lombok.RequiredArgsConstructor;
 
 @Service
+@RequiredArgsConstructor
 class RouteForwardingService {
 
-	public void route(RouteCriteria criteria, FormData formData) {
-//FIXME implement me
+	private final VorgangService vorgangService;
+
+	public void route(FormData formData) {
+		vorgangService.createVorgang(formData);
 	}
 }
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/ContentCollectorTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/ContentCollectorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4385eb7ef202481c49941943c58288129ec9804c
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/ContentCollectorTest.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+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.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
+import lombok.SneakyThrows;
+
+class ContentCollectorTest {
+
+	private ContentCollector contentCollector;
+	@Mock
+	private Function<InputStream, CompletableFuture<File>> fileSaver;
+	@Mock
+	private CompletableFuture<File> fileContentFuture;
+	private final IncomingFile incomingFile = IncomingFileTestFactory.createBuilder().file(null).build();
+
+	void setUpContentCollector() {
+		when(fileSaver.apply(any())).thenReturn(fileContentFuture);
+		contentCollector = spy(ContentCollector.builder().fileSaver(fileSaver).incomingFile(incomingFile).build());
+	}
+
+	@Nested
+	class TestBuilding {
+
+		private final byte[] content = new byte[] { 1, 2, 3 };
+
+		@Test
+		void shouldSetIncomingFile() {
+			setUpContentCollector();
+
+			assertThat(getIncomingFile()).isSameAs(incomingFile);
+		}
+
+		@Test
+		void shouldCreateInputStream() {
+			setUpContentCollector();
+
+			assertThat(getPipedInput()).isNotNull();
+		}
+
+		@Test
+		void shouldCreateOutputStream() {
+			setUpContentCollector();
+
+			assertThat(getPipedOutput()).isNotNull();
+		}
+
+		@Test
+		void shouldCreateConnectedStreams() {
+			setUpContentCollector();
+
+			verifyStreamSetUp();
+		}
+
+		@SneakyThrows
+		private void verifyStreamSetUp() {
+			var pipedInput = getPipedInput();
+			var pipedOutput = getPipedOutput();
+			pipedOutput.write(content);
+			pipedOutput.close();
+			var readBytes = pipedInput.readAllBytes();
+			assertThat(readBytes).isEqualTo(content);
+		}
+
+		@Test
+		void shouldCallFileSaver() {
+			setUpContentCollector();
+
+			verify(fileSaver).apply(getPipedInput());
+		}
+
+		@Test
+		void shouldSetReceivingFileContent() {
+			setUpContentCollector();
+
+			assertThat(getFileContent()).isSameAs(fileContentFuture);
+		}
+	}
+
+	@Nested
+	class TestCollect {
+
+		@Mock
+		private PipedOutputStream pipedOutput;
+
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			setPipedOutput(pipedOutput);
+		}
+
+		@Nested
+		class TestOnEndOfFile {
+
+			private final GrpcFileContent fileContent = GrpcFileContentTestFactory.createBuilder().setIsEndOfFile(true).build();
+			private final IncomingFile completedIncomingFile = IncomingFileTestFactory.create();
+
+			@BeforeEach
+			void setUp() {
+				doReturn(completedIncomingFile).when(contentCollector).handleEndOfFile();
+			}
+
+			@Test
+			void shouldCallHandleEndOfFile() {
+				contentCollector.collect(fileContent);
+
+				verify(contentCollector).handleEndOfFile();
+			}
+
+			@Test
+			@SneakyThrows
+			void shouldNotWriteContentToOutputStream() {
+				contentCollector.collect(fileContent);
+
+				verify(pipedOutput, never()).write(any());
+			}
+
+			@Test
+			void shouldReturnCompletedIncomingFile() {
+				var result = contentCollector.collect(fileContent);
+
+				assertThat(result).contains(completedIncomingFile);
+			}
+		}
+
+		@Nested
+		class TestOnNotEndOfFile {
+
+			private final GrpcFileContent fileContent = GrpcFileContentTestFactory.createBuilder().setIsEndOfFile(false).build();
+
+			@Test
+			@SneakyThrows
+			void shouldWriteContentToOutputStream() {
+				contentCollector.collect(fileContent);
+
+				verify(pipedOutput).write(GrpcFileContentTestFactory.CONTENT);
+			}
+
+			@Test
+			void shouldNotCallHandleEndOfFile() {
+				contentCollector.collect(fileContent);
+
+				verify(contentCollector, never()).handleEndOfFile();
+			}
+
+			@Test
+			void shouldReturnEmpty() {
+				var result = contentCollector.collect(fileContent);
+
+				assertThat(result).isEmpty();
+			}
+
+			@Test
+			@SneakyThrows
+			void shouldThrowTechnicalExceptionOnIOException() {
+				doThrow(new IOException()).when(pipedOutput).write(any());
+
+				assertThrows(TechnicalException.class, () -> {
+					contentCollector.collect(fileContent);
+				});
+			}
+		}
+	}
+
+	@Nested
+	class TestHandleEndOfFile {
+
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			doNothing().when(contentCollector).closeOutputPipe();
+			doReturn(IncomingFileTestFactory.FILE).when(contentCollector).getSavedFileContent();
+		}
+
+		@Test
+		void shouldCallCloseOutputPipe() {
+			contentCollector.handleEndOfFile();
+
+			verify(contentCollector).closeOutputPipe();
+		}
+
+		@Test
+		void shouldCallGetSavedFileContent() {
+			contentCollector.handleEndOfFile();
+
+			verify(contentCollector).getSavedFileContent();
+		}
+
+		@Test
+		void shouldReturnIncomingFileWithSavedFile() {
+			var returnedIncomingFile = contentCollector.handleEndOfFile();
+
+			assertThat(returnedIncomingFile).usingRecursiveComparison().isEqualTo(IncomingFileTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestGetSavedFileContent {
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			doNothing().when(contentCollector).closeInputPipe();
+		}
+
+		@Nested
+		class TestOnNoExceptions {
+			@Mock
+			private File fileContent;
+
+			@BeforeEach
+			@SneakyThrows
+			void setUp() {
+				when(fileContentFuture.get(anyLong(), any())).thenReturn(fileContent);
+			}
+
+			@Test
+			void shouldReturnFile() {
+				var savedFileContent = contentCollector.getSavedFileContent();
+
+				assertThat(savedFileContent).isSameAs(fileContent);
+			}
+
+			@Test
+			void shouldCallCloseInputPipe() {
+				contentCollector.getSavedFileContent();
+
+				verify(contentCollector).closeInputPipe();
+			}
+		}
+
+		@Nested
+		class TestOnExecutionException {
+
+			@BeforeEach
+			void setUp() {
+				setFileContent(CompletableFuture.failedFuture(new Exception()));
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> contentCollector.getSavedFileContent());
+			}
+
+			@Test
+			void shouldCallCloseInputPipe() {
+				try {
+					contentCollector.getSavedFileContent();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(contentCollector).closeInputPipe();
+			}
+		}
+
+		@Nested
+		class TestOnTimeoutException {
+
+			@Mock
+			private CompletableFuture<File> fileFuture;
+
+			@BeforeEach
+			@SneakyThrows
+			void setUp() {
+				setFileContent(fileFuture);
+				when(fileFuture.get(anyLong(), any())).thenThrow(new TimeoutException());
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> contentCollector.getSavedFileContent());
+			}
+
+			@Test
+			void shouldCallCloseInputPipe() {
+				try {
+					contentCollector.getSavedFileContent();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(contentCollector).closeInputPipe();
+			}
+		}
+
+		@Nested
+		class TestOnInterruptedException {
+
+			@Mock
+			private CompletableFuture<File> fileFuture;
+
+			@BeforeEach
+			@SneakyThrows
+			void setUp() {
+				setFileContent(fileFuture);
+				when(fileFuture.get(anyLong(), any())).thenThrow(new InterruptedException());
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> contentCollector.getSavedFileContent());
+			}
+
+			@Test
+			void shouldInterruptCurrentThread() {
+				try {
+					contentCollector.getSavedFileContent();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				assertThat(Thread.currentThread().isInterrupted()).isTrue();
+			}
+
+			@Test
+			void shouldCallCloseInputPipe() {
+				try {
+					contentCollector.getSavedFileContent();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(contentCollector).closeInputPipe();
+			}
+		}
+	}
+
+	@Nested
+	class TestClose {
+
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			doNothing().when(contentCollector).closeOutputPipe();
+			doNothing().when(contentCollector).closeInputPipe();
+		}
+
+		@Test
+		void shouldCallCloseOutputPipe() {
+			contentCollector.close();
+
+			verify(contentCollector).closeOutputPipe();
+		}
+
+		@Test
+		void shouldCallCloseInputPipe() {
+			contentCollector.close();
+
+			verify(contentCollector).closeInputPipe();
+		}
+	}
+
+	@Nested
+	class TestCloseOutputPipe {
+
+		@Mock
+		private PipedOutputStream pipedOutput;
+
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			setPipedOutput(pipedOutput);
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldClosePipedOutput() {
+			contentCollector.closeOutputPipe();
+
+			verify(pipedOutput).close();
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldNotThrowException() {
+			doThrow(IOException.class).when(pipedOutput).close();
+
+			assertDoesNotThrow(() -> contentCollector.closeOutputPipe());
+		}
+	}
+
+	@Nested
+	class TestCloseInputPipe {
+
+		@Mock
+		private PipedInputStream pipedInput;
+
+		@BeforeEach
+		void setUp() {
+			setUpContentCollector();
+			setPipedInput(pipedInput);
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldClosePipedInput() {
+			contentCollector.closeInputPipe();
+
+			verify(pipedInput).close();
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldNotThrowException() {
+			doThrow(IOException.class).when(pipedInput).close();
+
+			assertDoesNotThrow(() -> contentCollector.closeInputPipe());
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private CompletableFuture<File> getFileContent() {
+		return (CompletableFuture<File>) ReflectionTestUtils.getField(contentCollector, "receivingFileContent");
+	}
+
+	private void setFileContent(CompletableFuture<File> fileContent) {
+		ReflectionTestUtils.setField(contentCollector, "receivingFileContent", fileContent);
+	}
+
+	private PipedInputStream getPipedInput() {
+		return (PipedInputStream) ReflectionTestUtils.getField(contentCollector, "pipedInput");
+	}
+
+	private void setPipedInput(PipedInputStream pipedInput) {
+		ReflectionTestUtils.setField(contentCollector, "pipedInput", pipedInput);
+	}
+
+	private PipedOutputStream getPipedOutput() {
+		return (PipedOutputStream) ReflectionTestUtils.getField(contentCollector, "pipedOutput");
+	}
+
+	private void setPipedOutput(PipedOutputStream pipedOutput) {
+		ReflectionTestUtils.setField(contentCollector, "pipedOutput", pipedOutput);
+	}
+
+	private IncomingFile getIncomingFile() {
+		return (IncomingFile) ReflectionTestUtils.getField(contentCollector, "incomingFile");
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserverTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserverTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8e6dce6b1fbdc17bced5a05bc48dd74b981a501
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/EingangStubReceiverStreamObserverTest.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+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.io.File;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.common.formdata.IncomingFile;
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentation;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
+
+class EingangStubReceiverStreamObserverTest {
+
+	private EingangStubReceiverStreamObserver observer;
+
+	@Mock
+	private RouteForwardingMapper routeForwardingMapper;
+	@Mock
+	private IncomingFileMapper incomingFileMapper;
+	@Mock
+	private IncomingFileGroupMapper incomingFileGroupMapper;
+	@Mock
+	private Function<InputStream, CompletableFuture<File>> fileSaver;
+	@Mock
+	private Consumer<FormData> formDataConsumer;
+	@Mock
+	private Consumer<GrpcRouteForwardingResponse> responseConsumer;
+
+	@BeforeEach
+	void setUp() {
+		observer = spy(EingangStubReceiverStreamObserver.builder()
+				.fileSaver(fileSaver)
+				.routeForwardingMapper(routeForwardingMapper)
+				.incomingFileMapper(incomingFileMapper)
+				.incomingFileGroupMapper(incomingFileGroupMapper)
+				.formDataConsumer(formDataConsumer)
+				.responseConsumer(responseConsumer)
+				.build());
+	}
+
+	@Nested
+	class TestOnNext {
+
+		@Nested
+		class TestOnRouteForwarding {
+
+			@BeforeEach
+			void mock() {
+				doNothing().when(observer).handleRouteForwarding(any());
+			}
+
+			@Test
+			void shouldCallHandleRouteForwarding() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRouteForwarding());
+
+				verify(observer).handleRouteForwarding(GrpcRouteForwardingRequestTestFactory.ROUTE_FORWARDING);
+			}
+
+			@Test
+			void shouldNotCallHandleAttachment() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRouteForwarding());
+
+				verify(observer, never()).handleAttachment(any());
+			}
+
+			@Test
+			void shouldNotCallHandleRepresentation() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRouteForwarding());
+
+				verify(observer, never()).handleRepresentation(any());
+			}
+		}
+
+		@Nested
+		class TestOnAttachment {
+			@Test
+			void shouldCallHandleAttachment() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithAttachment());
+
+				verify(observer).handleAttachment(GrpcRouteForwardingRequestTestFactory.ATTACHMENT);
+			}
+
+			@Test
+			void shouldNotCallHandleRouteForwarding() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithAttachment());
+
+				verify(observer, never()).handleRouteForwarding(any());
+			}
+
+			@Test
+			void shouldNotCallHandleRepresentation() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithAttachment());
+
+				verify(observer, never()).handleRepresentation(any());
+			}
+		}
+
+		@Nested
+		class TestOnRepresentation {
+			@Test
+			void shouldCallHandleRepresentation() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRepresentation());
+
+				verify(observer).handleRepresentation(GrpcRouteForwardingRequestTestFactory.REPRESENTATION);
+			}
+
+			@Test
+			void shouldNotCallHandleRouteForwarding() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRepresentation());
+
+				verify(observer, never()).handleRouteForwarding(any());
+			}
+
+			@Test
+			void shouldNotCallHandleAttachment() {
+				observer.onNext(GrpcRouteForwardingRequestTestFactory.createWithRepresentation());
+
+				verify(observer, never()).handleAttachment(any());
+			}
+		}
+	}
+
+	@Nested
+	class TestHandleRouteForwarding {
+
+		private final GrpcRouteForwarding routeForwarding = GrpcRouteForwardingTestFactory.create();
+
+		@Test
+		void shouldThrowIllegalStateExceptionIfFormDataIsSet() {
+			setFormData(FormDataTestFactory.create());
+
+			assertThrows(IllegalStateException.class, () -> observer.handleRouteForwarding(routeForwarding));
+
+		}
+
+		@Test
+		void shouldMapRouteForwarding() {
+			observer.handleRouteForwarding(routeForwarding);
+
+			verify(routeForwardingMapper).toFormData(routeForwarding);
+		}
+
+		@Test
+		void shouldSetFormData() {
+			var formData = FormDataTestFactory.create();
+			when(routeForwardingMapper.toFormData(any())).thenReturn(formData);
+
+			observer.handleRouteForwarding(routeForwarding);
+
+			assertThat(getFormData()).isEqualTo(formData);
+		}
+	}
+
+	@Nested
+	class TestHandleAttachment {
+
+		@Mock
+		private ContentCollector contentCollector;
+
+		@Nested
+		class TestWithFile {
+
+			private final GrpcAttachment attachmentWithFile = GrpcAttachmentTestFactory.createWithFile();
+			private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+			@BeforeEach
+			void initMock() {
+				when(incomingFileMapper.fromGrpcAttachmentFile(any())).thenReturn(incomingFile);
+				doNothing().when(observer).verifyNoOngoingFileDownload();
+			}
+
+			@Test
+			void shouldCallVerifyNoOngoingFileDownload() {
+				observer.handleAttachment(attachmentWithFile);
+
+				verify(observer).verifyNoOngoingFileDownload();
+			}
+
+			@Test
+			void shouldCallIncomingFileMapper() {
+				observer.handleAttachment(attachmentWithFile);
+
+				verify(incomingFileMapper).fromGrpcAttachmentFile(GrpcAttachmentTestFactory.FILE);
+			}
+
+			@Test
+			void shouldSetContentCollector() {
+				var expectedContentCollector = ContentCollector.builder().fileSaver(fileSaver).incomingFile(incomingFile).build();
+
+				observer.handleAttachment(attachmentWithFile);
+
+				assertThat(getContentCollector()).usingRecursiveComparison().ignoringFields("pipedInput", "pipedOutput")
+						.isEqualTo(expectedContentCollector);
+			}
+
+			@Test
+			void shouldSetGroupName() {
+				observer.handleAttachment(attachmentWithFile);
+
+				assertThat(getGroupName()).isEqualTo(GrpcAttachmentFileTestFactory.GROUP_NAME);
+			}
+
+			@Test
+			void shouldNotCollectContent() {
+				observer.handleAttachment(attachmentWithFile);
+
+				verify(contentCollector, never()).collect(any());
+			}
+		}
+
+		@Nested
+		class TestWithContent {
+
+			private final GrpcAttachment attachmentWithContent = GrpcAttachmentTestFactory.createWithContent();
+			private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+			@BeforeEach
+			void setUp() {
+				setContentCollector(contentCollector);
+			}
+
+			@Test
+			void shouldCallVerifyMetadataWasSent() {
+				observer.handleAttachment(attachmentWithContent);
+
+				verify(observer).verifyMetadataWasSent();
+			}
+
+			@Test
+			void shouldCollectContent() {
+				observer.handleAttachment(attachmentWithContent);
+
+				verify(contentCollector).collect(GrpcAttachmentTestFactory.CONTENT);
+			}
+
+			@Test
+			void shouldNotCallIncomingFileMapper() {
+				observer.handleAttachment(attachmentWithContent);
+
+				verify(incomingFileMapper, never()).fromGrpcAttachmentFile(any());
+			}
+
+			@Test
+			void shouldNotSetGroupName() {
+				observer.handleAttachment(attachmentWithContent);
+
+				assertThat(getGroupName()).isNull();
+			}
+
+			@Test
+			void shouldCallAddAsAttachment() {
+				when(contentCollector.collect(any())).thenReturn(Optional.of(incomingFile));
+
+				observer.handleAttachment(attachmentWithContent);
+
+				verify(observer).addAsAttachment(incomingFile);
+			}
+
+			@Test
+			void shouldNotCallAddAsAttachment() {
+				when(contentCollector.collect(any())).thenReturn(Optional.empty());
+
+				observer.handleAttachment(attachmentWithContent);
+
+				verify(observer, never()).addAsAttachment(any());
+			}
+
+		}
+	}
+
+	@Nested
+	class TestHandleRepresentation {
+
+		@Mock
+		private ContentCollector contentCollector;
+
+		@Nested
+		class TestWithFile {
+
+			private final GrpcRepresentation representationWithFile = GrpcRepresentationTestFactory.createWithFile();
+			private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+			@BeforeEach
+			void mock() {
+				when(incomingFileMapper.fromGrpcRepresentationFile(any())).thenReturn(incomingFile);
+				doNothing().when(observer).verifyNoOngoingFileDownload();
+			}
+
+			@Test
+			void shouldCallVerifyNoOngoingFileDownload() {
+				observer.handleRepresentation(representationWithFile);
+
+				verify(observer).verifyNoOngoingFileDownload();
+			}
+
+			@Test
+			void shouldCallIncomingFileMapper() {
+				observer.handleRepresentation(representationWithFile);
+
+				verify(incomingFileMapper).fromGrpcRepresentationFile(GrpcRepresentationTestFactory.FILE);
+			}
+
+			@Test
+			void shouldSetContentCollector() {
+				var expectedContentCollector = ContentCollector.builder().fileSaver(fileSaver).incomingFile(incomingFile).build();
+
+				observer.handleRepresentation(representationWithFile);
+
+				assertThat(getContentCollector()).usingRecursiveComparison().ignoringFields("pipedInput", "pipedOutput")
+						.isEqualTo(expectedContentCollector);
+			}
+
+			@Test
+			void shouldNotCollectContent() {
+				observer.handleRepresentation(representationWithFile);
+
+				verify(contentCollector, never()).collect(any());
+			}
+		}
+
+		@Nested
+		class TestWithContent {
+
+			private final GrpcRepresentation representationWithContent = GrpcRepresentationTestFactory.createWithContent();
+			private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+			@BeforeEach
+			void setUp() {
+				setContentCollector(contentCollector);
+			}
+
+			@Test
+			void shouldCallVerifyMetadataWasSent() {
+				observer.handleRepresentation(representationWithContent);
+
+				verify(observer).verifyMetadataWasSent();
+			}
+
+			@Test
+			void shouldCollectContent() {
+				observer.handleRepresentation(representationWithContent);
+
+				verify(contentCollector).collect(GrpcRepresentationTestFactory.CONTENT);
+			}
+
+			@Test
+			void shouldNotCallIncomingFileMapper() {
+				observer.handleRepresentation(representationWithContent);
+
+				verify(incomingFileMapper, never()).fromGrpcRepresentationFile(any());
+			}
+
+			@Test
+			void shouldCallAddAsRepresentation() {
+				when(contentCollector.collect(any())).thenReturn(Optional.of(incomingFile));
+
+				observer.handleRepresentation(representationWithContent);
+
+				verify(observer).addAsRepresentation(incomingFile);
+			}
+
+			@Test
+			void shouldNotCallAddAsRepresentation() {
+				when(contentCollector.collect(any())).thenReturn(Optional.empty());
+
+				observer.handleRepresentation(representationWithContent);
+
+				verify(observer, never()).addAsRepresentation(any());
+			}
+		}
+	}
+
+	@Nested
+	class TestVerifyNoOngoingFileDownload {
+
+		@Test
+		void shouldThrowIllegalStateExceptionIfContentCollectorIsNotNull() {
+			setContentCollector(mock(ContentCollector.class));
+
+			assertThrows(IllegalStateException.class, () -> observer.verifyNoOngoingFileDownload());
+		}
+
+		@Test
+		void shouldNotThrowExceptionIfContentCollectorIsNull() {
+			assertDoesNotThrow(() -> observer.verifyNoOngoingFileDownload());
+		}
+	}
+
+	@Nested
+	class TestVerifyMetadataWasSent {
+
+		@Test
+		void shouldThrowIllegalStateExceptionIfContentCollectorIsNull() {
+			assertThrows(IllegalStateException.class, () -> observer.verifyMetadataWasSent());
+		}
+
+		@Test
+		void shouldNotThrowExceptionIfContentCollectorIsNotNull() {
+			setContentCollector(mock(ContentCollector.class));
+
+			assertDoesNotThrow(() -> observer.verifyMetadataWasSent());
+		}
+	}
+
+	@Nested
+	class TestAddAsAttachment {
+
+		@Mock
+		private ContentCollector contentCollector;
+
+		private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+		@BeforeEach
+		void setUp() {
+			setContentCollector(contentCollector);
+			setGroupName(GrpcAttachmentFileTestFactory.GROUP_NAME);
+		}
+
+		@Test
+		void shouldAddFileToAttachments() {
+			observer.addAsAttachment(incomingFile);
+
+			var attachmentGroup = getAttachments().get(GrpcAttachmentFileTestFactory.GROUP_NAME);
+			assertThat(attachmentGroup).usingRecursiveFieldByFieldElementComparator().containsExactly(incomingFile);
+		}
+
+		@Test
+		void shouldSetContentCollectorToNull() {
+			observer.addAsAttachment(incomingFile);
+
+			assertThat(getContentCollector()).isNull();
+		}
+	}
+
+	@Nested
+	class TestAddAsRepresentation {
+
+		@Mock
+		private ContentCollector contentCollector;
+
+		private final IncomingFile incomingFile = IncomingFileTestFactory.create();
+
+		@BeforeEach
+		void setUp() {
+			setContentCollector(contentCollector);
+		}
+
+		@Test
+		void shouldAddFileToRepresentations() {
+			observer.addAsRepresentation(incomingFile);
+
+			assertThat(getRepresentations()).usingRecursiveFieldByFieldElementComparator().containsExactly(incomingFile);
+		}
+
+		@Test
+		void shouldSetContentCollectorToNull() {
+			observer.addAsRepresentation(incomingFile);
+
+			assertThat(getContentCollector()).isNull();
+		}
+	}
+
+	@Nested
+	class TestOnError {
+
+		@Mock
+		private ContentCollector contentCollector;
+
+		@BeforeEach
+		void mock() {
+			setContentCollector(contentCollector);
+			setContentCollector(contentCollector);
+		}
+
+		@Test
+		void shouldCloseContentCollector() {
+			observer.onError(new Exception());
+
+			verify(contentCollector).close();
+		}
+	}
+
+	@Nested
+	class TestOnCompleted {
+
+		private final FormData formData = FormDataTestFactory.create();
+
+		@BeforeEach
+		void mock() {
+			doReturn(formData).when(observer).assembleFormData();
+		}
+
+		@Test
+		void shouldCallAssembleFormData() {
+			observer.onCompleted();
+
+			verify(observer).assembleFormData();
+		}
+
+		@Test
+		void shouldCallFormDataConsumer() {
+			observer.onCompleted();
+
+			verify(formDataConsumer).accept(formData);
+		}
+
+		@Test
+		void shouldCallResponseConsumer() {
+			observer.onCompleted();
+
+			verify(responseConsumer).accept(GrpcRouteForwardingResponse.getDefaultInstance());
+		}
+	}
+
+	@Nested
+	class TestAssembleFormData {
+
+		@Nested
+		class TestOnFormDataNotSet {
+
+			@Test
+			void shouldThrowIllegalStateException() {
+				assertThrows(IllegalStateException.class, () -> observer.assembleFormData());
+			}
+		}
+
+		@Nested
+		class TestOnFormDataSet {
+
+			private final FormData formData = FormDataTestFactory.createBuilder()
+					.clearAttachments()
+					.clearRepresentations()
+					.numberOfAttachments(0)
+					.numberOfRepresentations(0)
+					.control(null)
+					.build();
+
+			private final List<IncomingFile> representations = FormDataTestFactory.REPRESENTATIONS;
+			private final Map.Entry<String, List<IncomingFile>> attachmentEntry = Map.entry(
+					IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.INCOMING_FILES);
+
+			@BeforeEach
+			void setUp() {
+				setFormData(formData);
+				setRepresentations(representations);
+				setAttachments(Map.ofEntries(attachmentEntry));
+			}
+
+			@Test
+			void shouldCallIncomingFileGroupMapper() {
+				observer.assembleFormData();
+
+				verify(incomingFileGroupMapper).fromMapEntry(attachmentEntry);
+			}
+
+			@Test
+			void shouldReturnFormData() {
+				var expectedFormData = FormDataTestFactory.createBuilder()
+						.control(null)
+						.build();
+				when(incomingFileGroupMapper.fromMapEntry(attachmentEntry)).thenReturn(IncomingFileGroupTestFactory.create());
+
+				var assembledFormData = observer.assembleFormData();
+
+				assertThat(assembledFormData).usingRecursiveComparison().isEqualTo(expectedFormData);
+			}
+		}
+	}
+
+	private FormData getFormData() {
+		return (FormData) ReflectionTestUtils.getField(observer, "formData");
+	}
+
+	private void setFormData(FormData formData) {
+		ReflectionTestUtils.setField(observer, "formData", formData);
+	}
+
+	private String getGroupName() {
+		return (String) ReflectionTestUtils.getField(observer, "groupName");
+	}
+
+	private void setGroupName(String groupName) {
+		ReflectionTestUtils.setField(observer, "groupName", groupName);
+	}
+
+	@SuppressWarnings("unchecked")
+	private List<IncomingFile> getRepresentations() {
+		return (List<IncomingFile>) ReflectionTestUtils.getField(observer, "representations");
+	}
+
+	private void setRepresentations(List<IncomingFile> representations) {
+		ReflectionTestUtils.setField(observer, "representations", representations);
+	}
+
+	@SuppressWarnings("unchecked")
+	private Map<String, List<IncomingFile>> getAttachments() {
+		return (Map<String, List<IncomingFile>>) ReflectionTestUtils.getField(observer, "attachments");
+	}
+
+	private void setAttachments(Map<String, List<IncomingFile>> attachments) {
+		ReflectionTestUtils.setField(observer, "attachments", attachments);
+	}
+
+	private ContentCollector getContentCollector() {
+		return (ContentCollector) ReflectionTestUtils.getField(observer, "contentCollector");
+	}
+
+	private void setContentCollector(ContentCollector contentCollector) {
+		ReflectionTestUtils.setField(observer, "contentCollector", contentCollector);
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/FileServiceTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/FileServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbf23b168f5d88dd004762a2bafc799799ed161c
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/FileServiceTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.io.ByteArrayInputStream;
+import java.nio.file.Files;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Spy;
+
+import com.thedeanda.lorem.LoremIpsum;
+
+import lombok.SneakyThrows;
+
+class FileServiceTest {
+
+	@Spy
+	private FileService service;
+
+	@Nested
+	class TestSaveToFile {
+
+		private final byte[] content = LoremIpsum.getInstance().getWords(100).getBytes();
+
+		@Test
+		@SneakyThrows
+		void shouldReturnFutureOfFile() {
+			try (var inputStream = new ByteArrayInputStream(content);) {
+				var fileFuture = service.saveToFile(inputStream);
+
+				var fileContent = Files.readAllBytes(fileFuture.get().toPath());
+				assertThat(fileContent).isEqualTo(content);
+
+			}
+		}
+
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAntragstellerTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAntragstellerTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..c125f88655416d9d9e9da4ed195be2e034a5c2cc
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAntragstellerTestFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.common.formdata.AntragstellerTestFactory;
+import de.ozgcloud.vorgang.vorgang.GrpcAntragsteller;
+import de.ozgcloud.vorgang.vorgang.GrpcFormData;
+import de.ozgcloud.vorgang.vorgang.GrpcFormField;
+import de.ozgcloud.vorgang.vorgang.GrpcSubForm;
+
+public class GrpcAntragstellerTestFactory {
+
+	public static final String ANREDE = AntragstellerTestFactory.ANREDE;
+	public static final String NACHNAME = AntragstellerTestFactory.NACHNAME;
+	public static final String VORNAME = AntragstellerTestFactory.VORNAME;
+	public static final String GEBURTSDATUM = AntragstellerTestFactory.GEBURTSDATUM;
+	public static final String GEBURTSORT = AntragstellerTestFactory.GEBURTSORT;
+	public static final String GEBURTSNAME = AntragstellerTestFactory.GEBURTSNAME;
+	public static final String EMAIL = AntragstellerTestFactory.EMAIL;
+	public static final String TELEFON = AntragstellerTestFactory.TELEFON;
+	public static final String STRASSE = AntragstellerTestFactory.STRASSE;
+	public static final String HAUSNUMMER = AntragstellerTestFactory.HAUSNUMMER;
+	public static final String PLZ = AntragstellerTestFactory.PLZ;
+	public static final String ORT = AntragstellerTestFactory.ORT;
+
+	public static final String POSTFACH_ID = AntragstellerTestFactory.POSTFACH_ID;
+	public static final GrpcFormData OTHER_DATA = GrpcFormData.newBuilder()
+			.addField(GrpcFormField.newBuilder()
+					.setName(AntragstellerTestFactory.GEBIET_BEZEICHNUNG_KEY)
+					.setValue(AntragstellerTestFactory.GEBIET_BEZEICHNUNG_VALUE).build())
+			.addForm(GrpcSubForm.newBuilder().setTitle(AntragstellerTestFactory.SUB_PERSOENLICHE_EIGNUNG)
+					.addField(GrpcFormField.newBuilder()
+							.setName(AntragstellerTestFactory.SUB_VERBOTENE_VEREINIGUNG_KEY)
+							.setValue(AntragstellerTestFactory.SUB_VERBOTENE_VEREINIGUNG_VALUE).build())
+					.build())
+			.build();
+
+	public static GrpcAntragsteller create() {
+		return createBuilder().build();
+	}
+
+	public static GrpcAntragsteller.Builder createBuilder() {
+		return GrpcAntragsteller.newBuilder()
+				.setAnrede(ANREDE)
+				.setFirmaName(AntragstellerTestFactory.FIRMA_NAME)
+				.setGeburtsdatum(GEBURTSDATUM)
+				.setNachname(NACHNAME)
+				.setVorname(VORNAME)
+				.setGeburtsname(GEBURTSNAME)
+				.setGeburtsort(GEBURTSORT)
+				.setEmail(EMAIL)
+				.setTelefon(TELEFON)
+				.setStrasse(STRASSE)
+				.setHausnummer(HAUSNUMMER)
+				.setPlz(PLZ)
+				.setOrt(ORT)
+				.setPostfachId(POSTFACH_ID)
+				.setOtherData(OTHER_DATA);
+	}
+}
\ No newline at end of file
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentFileTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentFileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec48f44e48beb0c2d0cd072050bd7494a1effac3
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentFileTestFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile;
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile.Builder;
+
+public class GrpcAttachmentFileTestFactory {
+
+	public static final String GROUP_NAME = IncomingFileGroupTestFactory.NAME;
+
+	public static GrpcAttachmentFile create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcAttachmentFile.newBuilder()
+				.setGroupName(GROUP_NAME)
+				.setFileName(IncomingFileTestFactory.NAME)
+				.setContentType(IncomingFileTestFactory.CONTENT_TYPE)
+				.setVendorId(IncomingFileTestFactory.VENDOR_ID)
+				.setSize(IncomingFileTestFactory.SIZE);
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5593e13f415f71ceee9fd21e5fb548383f7d8eb
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcAttachmentTestFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile;
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
+
+public class GrpcAttachmentTestFactory {
+
+	public static final GrpcAttachmentFile FILE = GrpcAttachmentFileTestFactory.create();
+	public static final GrpcFileContent CONTENT = GrpcFileContentTestFactory.create();
+
+	public static GrpcAttachment createWithFile() {
+		return GrpcAttachment.newBuilder()
+				.setFile(FILE)
+				.build();
+	}
+
+	public static GrpcAttachment createWithContent() {
+		return GrpcAttachment.newBuilder()
+				.setContent(CONTENT)
+				.build();
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcEingangStubTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcEingangStubTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1fc1c66423606d998d54ee9a620cb736bc2a3aa
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcEingangStubTestFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.forwarding.GrpcEingangStub;
+import de.ozgcloud.eingang.forwarding.GrpcEingangStub.Builder;
+import de.ozgcloud.eingang.router.GrpcEingangHeaderTestFactory;
+import de.ozgcloud.vorgang.common.grpc.GrpcFormDataTestFactory;
+import de.ozgcloud.vorgang.vorgang.GrpcAntragsteller;
+import de.ozgcloud.vorgang.vorgang.GrpcEingangHeader;
+import de.ozgcloud.vorgang.vorgang.GrpcFormData;
+
+public class GrpcEingangStubTestFactory {
+
+	public static final GrpcEingangHeader HEADER = GrpcEingangHeaderTestFactory.create();
+	public static final GrpcAntragsteller ANTRAGSTELLER = GrpcAntragstellerTestFactory.create();
+	public static final GrpcFormData FORM_DATA = GrpcFormDataTestFactory.create();
+
+	public static GrpcEingangStub create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcEingangStub.newBuilder()
+				.setHeader(HEADER)
+				.setAntragsteller(ANTRAGSTELLER)
+				.setFormData(FORM_DATA);
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcFileContentTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcFileContentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4040f97517ce4c6a379d38eaeb173bc96bf90d0
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcFileContentTestFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import com.google.protobuf.ByteString;
+import com.thedeanda.lorem.LoremIpsum;
+
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
+import de.ozgcloud.eingang.forwarding.GrpcFileContent.Builder;
+
+public class GrpcFileContentTestFactory {
+
+	public static final boolean IS_END_OF_FILE = false;
+	public static final byte[] CONTENT = LoremIpsum.getInstance().getWords(10).getBytes();
+
+	public static GrpcFileContent create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcFileContent.newBuilder()
+				.setContent(ByteString.copyFrom(CONTENT))
+				.setIsEndOfFile(IS_END_OF_FILE);
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationFileTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationFileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5155199a28dc9e762cce685495fadc220eb5eec
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationFileTestFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentationFile;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentationFile.Builder;
+
+public class GrpcRepresentationFileTestFactory {
+
+	public static GrpcRepresentationFile create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcRepresentationFile.newBuilder()
+				.setFileName(IncomingFileTestFactory.NAME)
+				.setContentType(IncomingFileTestFactory.CONTENT_TYPE)
+				.setVendorId(IncomingFileTestFactory.VENDOR_ID)
+				.setSize(IncomingFileTestFactory.SIZE);
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e437c900d0a49cf0f714d4d7718a9f699d681af8
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRepresentationTestFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentation;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentationFile;
+
+public class GrpcRepresentationTestFactory {
+
+	public static final GrpcRepresentationFile FILE = GrpcRepresentationFileTestFactory.create();
+	public static final GrpcFileContent CONTENT = GrpcFileContentTestFactory.create();
+
+	public static GrpcRepresentation createWithFile() {
+		return GrpcRepresentation.newBuilder()
+				.setFile(FILE)
+				.build();
+	}
+
+	public static GrpcRepresentation createWithContent() {
+		return GrpcRepresentation.newBuilder()
+				.setContent(CONTENT)
+				.build();
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteCriteriaTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteCriteriaTestFactory.java
similarity index 53%
rename from forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteCriteriaTestFactory.java
rename to forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteCriteriaTestFactory.java
index f5b8c2742f66a3ac5119e5f1965e8751f3655c9b..450f68612ac6d32959aba874a3b7e0a5fa31932f 100644
--- a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteCriteriaTestFactory.java
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteCriteriaTestFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -23,35 +23,22 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
-import java.util.Optional;
+import java.util.UUID;
 
 import de.ozgcloud.eingang.forwarding.GrpcRouteCriteria;
+import de.ozgcloud.eingang.forwarding.GrpcRouteCriteria.Builder;
 
-public class RouteCriteriaTestFactory {
+public class GrpcRouteCriteriaTestFactory {
 
-	public static final String GEMEINDE_SCHLUSSEL = "0815";
-	public static final String WEBSERVICE_URL = "http://nimmerland.by.kop-cloud.de/ws";
-	public static final String ORGANISATIONSEINHEITEN_ID = "4711";
+	public static final String ORGANISATION_EINHEIT_ID = UUID.randomUUID().toString();
 
-	public static RouteCriteria create() {
+	public static GrpcRouteCriteria create() {
 		return createBuilder().build();
 	}
 
-	public static RouteCriteria.RouteCriteriaBuilder createBuilder() {
-		return RouteCriteria.builder()
-				.gemeindeSchluessel(Optional.of(GEMEINDE_SCHLUSSEL))
-				.webserviceUrl(Optional.of(WEBSERVICE_URL))
-				.organisationseinheitenId(Optional.of(ORGANISATIONSEINHEITEN_ID));
-	}
-
-	public static GrpcRouteCriteria createGrpc() {
-		return createGrpcBuilder().build();
-	}
-
-	public static GrpcRouteCriteria.Builder createGrpcBuilder() {
+	public static Builder createBuilder() {
 		return GrpcRouteCriteria.newBuilder()
-				.setGemeindeSchluessel(GEMEINDE_SCHLUSSEL)
-				.setWebserviceUrl(WEBSERVICE_URL)
-				.setOrganisationseinheitenId(ORGANISATIONSEINHEITEN_ID);
+				.setOrganisationEinheitId(ORGANISATION_EINHEIT_ID);
 	}
+
 }
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingRequestTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingRequestTestFactory.java
index 03c5beaeddc4b13c6dd1f8ad51459f63097bd951..a05ac9a84aa68f8deae2657d2e4e64fd37879291 100644
--- a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingRequestTestFactory.java
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingRequestTestFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -23,16 +23,32 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
+import de.ozgcloud.eingang.forwarding.GrpcRepresentation;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 
 public class GrpcRouteForwardingRequestTestFactory {
 
-	public static GrpcRouteForwardingRequest create() {
-		return createBuilder().build();
+	public static final GrpcRepresentation REPRESENTATION = GrpcRepresentationTestFactory.createWithFile();
+	public static final GrpcAttachment ATTACHMENT = GrpcAttachmentTestFactory.createWithFile();
+	public static final GrpcRouteForwarding ROUTE_FORWARDING = GrpcRouteForwardingTestFactory.create();
+
+	public static GrpcRouteForwardingRequest createWithRouteForwarding() {
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setRouteForwarding(ROUTE_FORWARDING)
+				.build();
+	}
+
+	public static GrpcRouteForwardingRequest createWithAttachment() {
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setAttachment(ATTACHMENT)
+				.build();
 	}
 
-	public static GrpcRouteForwardingRequest.Builder createBuilder() {
+	public static GrpcRouteForwardingRequest createWithRepresentation() {
 		return GrpcRouteForwardingRequest.newBuilder()
-				.setRouteCriteria(RouteCriteriaTestFactory.createGrpc());
+				.setRepresentation(REPRESENTATION)
+				.build();
 	}
 }
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingTestFactory.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca29d219b92c0c9a0c1f7838bae3286059d8dd23
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/GrpcRouteForwardingTestFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import de.ozgcloud.eingang.forwarding.GrpcEingangStub;
+import de.ozgcloud.eingang.forwarding.GrpcRouteCriteria;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding.Builder;
+
+public class GrpcRouteForwardingTestFactory {
+
+	public static final GrpcRouteCriteria ROUTE_CRITERIA = GrpcRouteCriteriaTestFactory.create();
+	public static final GrpcEingangStub EINGANG_STUB = GrpcEingangStubTestFactory.create();
+
+	public static GrpcRouteForwarding create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcRouteForwarding.newBuilder()
+				.setEingangStub(EINGANG_STUB)
+				.setRouteCriteria(ROUTE_CRITERIA);
+	}
+
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapperTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b86144b64161e91f58368ca6f3ff92c93d0a0e1f
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileGroupMapperTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
+
+class IncomingFileGroupMapperTest {
+
+	private final IncomingFileGroupMapper mapper = Mappers.getMapper(IncomingFileGroupMapper.class);
+
+	@Nested
+	class TestFromMapEntry {
+
+		@Test
+		void shouldMapToIncomingFileGroup() {
+			var entry = Map.entry(IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.INCOMING_FILES);
+
+			var incomingFileGroup = mapper.fromMapEntry(entry);
+
+			assertThat(incomingFileGroup).usingRecursiveComparison().isEqualTo(IncomingFileGroupTestFactory.create());
+		}
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileMapperTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8b12b9f1cb7ef9adc9b6e37b616ad3dbe59bce2
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/IncomingFileMapperTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+
+import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
+
+class IncomingFileMapperTest {
+
+	private final IncomingFileMapper mapper = Mappers.getMapper(IncomingFileMapper.class);
+
+	@Nested
+	class TestFromGrpcRepresentationFile {
+		@Test
+		void shouldMapToIncomingFile() {
+			var grpcRepresentationFile = GrpcRepresentationFileTestFactory.create();
+
+			var incomingFile = mapper.fromGrpcRepresentationFile(grpcRepresentationFile);
+
+			assertThat(incomingFile).usingRecursiveComparison().ignoringFields("id", "file")
+					.isEqualTo(IncomingFileTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestFromGrpcAttachmentFile {
+		@Test
+		void shouldMapToIncomingFile() {
+			var grpcAttachmentFile = GrpcAttachmentFileTestFactory.create();
+
+			var incomingFile = mapper.fromGrpcAttachmentFile(grpcAttachmentFile);
+
+			assertThat(incomingFile).usingRecursiveComparison().ignoringFields("id", "file")
+					.isEqualTo(IncomingFileTestFactory.create());
+		}
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
index e4dd78ddb466c0538814676dd3f8587fa550782c..35ac8140ce6cc4b7acd21cca3f6a0e15b06649dc 100644
--- a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
@@ -27,63 +27,182 @@ import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
-import org.mapstruct.factory.Mappers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import com.thedeanda.lorem.LoremIpsum;
 
+import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
-import de.ozgcloud.eingang.router.GrpcEingangMapper;
 import io.grpc.stub.StreamObserver;
 
 class RouteForwardingGrpcServiceTest {
 
 	@InjectMocks
+	@Spy
 	private RouteForwardingGrpcService service;
 
 	@Mock
 	private StreamObserver<GrpcRouteForwardingResponse> responseObserver;
 
 	@Mock
-	private RouteForwardingService routeService;
-	@Spy
-	private RouteCriteriaMapper criteriaMapper = Mappers.getMapper(RouteCriteriaMapper.class);
-	@Spy
-	private GrpcEingangMapper eingangMapper = Mappers.getMapper(GrpcEingangMapper.class);
+	private RouteForwardingMapper routeForwardingMapper;
+	@Mock
+	private IncomingFileMapper incomingFileMapper;
+	@Mock
+	private IncomingFileGroupMapper incomingFileGroupMapper;
+	@Mock
+	private RouteForwardingService routeForwardingService;
+	@Mock
+	private FileService fileService;
 
 	@Nested
 	class TestRouteForwarding {
+		private static final byte[] CONTENT = LoremIpsum.getInstance().getWords(5).getBytes();
+
+		private static final ByteArrayInputStream INPUT_STREAM = new ByteArrayInputStream(CONTENT);
 
-		@Captor
-		private ArgumentCaptor<RouteCriteria> criteriaCaptor;
+		private final FormData formData = FormDataTestFactory.create();
 
 		@Test
-		void shouldCallService() {
-			service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(), responseObserver);
+		void shouldSetFileSaver() {
+			var observer = service.routeForwarding(responseObserver);
 
-			verify(routeService).route(any(), any());
+			callFileSaver(observer);
+			verify(service).saveFile(INPUT_STREAM);
 		}
 
 		@Test
-		void shouldHaveRouteCriteria() {
-			service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(), responseObserver);
+		void shouldSetRouteForwardingMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getRouteForwardingMapper(observer)).isEqualTo(routeForwardingMapper);
+		}
+
+		@Test
+		void shouldSetIncomingFileMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getIncomingFileMapper(observer)).isEqualTo(incomingFileMapper);
+		}
+
+		@Test
+		void shouldSetIncomingFileGroupMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getIncomingFileGroupMapper(observer)).isEqualTo(incomingFileGroupMapper);
+		}
+
+		@Test
+		void shouldSetFormDataConsumer() {
+			var observer = service.routeForwarding(responseObserver);
+
+			callFormDataConsumer(observer, formData);
+			verify(routeForwardingService).route(formData);
+		}
+
+		@Test
+		void shouldSetResponseConsumer() {
+			var observer = service.routeForwarding(responseObserver);
+
+			callResponseConsumer(observer, GrpcRouteForwardingResponse.getDefaultInstance());
+			verify(service).respondWith(responseObserver, GrpcRouteForwardingResponse.getDefaultInstance());
+		}
+
+		@SuppressWarnings("unchecked")
+		private void callFileSaver(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var fileSaver = (Function<InputStream, CompletableFuture<File>>) ReflectionTestUtils.getField(uploadObserver, "fileSaver");
+			fileSaver.apply(INPUT_STREAM);
+		}
+
+		private RouteForwardingMapper getRouteForwardingMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var routeForwardingMapper = (RouteForwardingMapper) ReflectionTestUtils.getField(uploadObserver, "routeForwardingMapper");
+			return routeForwardingMapper;
+		}
+
+		private IncomingFileMapper getIncomingFileMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var incomingFileMapper = (IncomingFileMapper) ReflectionTestUtils.getField(uploadObserver, "incomingFileMapper");
+			return incomingFileMapper;
+		}
 
-			verify(routeService).route(criteriaCaptor.capture(), any());
+		private IncomingFileGroupMapper getIncomingFileGroupMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var incomingFileGroupMapper = (IncomingFileGroupMapper) ReflectionTestUtils.getField(uploadObserver, "incomingFileGroupMapper");
+			return incomingFileGroupMapper;
+		}
 
-			assertThat(criteriaCaptor.getValue()).usingRecursiveComparison().isEqualTo(RouteCriteriaTestFactory.create());
+		@SuppressWarnings("unchecked")
+		private void callFormDataConsumer(StreamObserver<GrpcRouteForwardingRequest> uploadObserver, FormData formData) {
+			var formDataConsumer = (Consumer<FormData>) ReflectionTestUtils.getField(uploadObserver, "formDataConsumer");
+			formDataConsumer.accept(formData);
 		}
 
+		@SuppressWarnings("unchecked")
+		private void callResponseConsumer(StreamObserver<GrpcRouteForwardingRequest> uploadObserver, GrpcRouteForwardingResponse response) {
+			var responseConsumer = (Consumer<GrpcRouteForwardingResponse>) ReflectionTestUtils.getField(uploadObserver, "responseConsumer");
+			responseConsumer.accept(response);
+		}
+	}
+
+	@Nested
+	class TestSaveFile {
+
+		@Mock
+		private InputStream inputStream;
+		@Mock
+		private CompletableFuture<File> fileFuture;
+
 		@Test
-		void shouldHaveFormData() {
-			service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(), responseObserver);
+		void shouldReturnNull() {
+			service.saveFile(inputStream);
 
-			verify(routeService).route(any(), notNull());
-			verify(eingangMapper).toFormData(any());
+			verify(fileService).saveToFile(inputStream);
+		}
+
+		@Test
+		void shouldReturnFileFuture() {
+			when(fileService.saveToFile(any())).thenReturn(fileFuture);
+
+			var result = service.saveFile(inputStream);
+
+			assertThat(result).isSameAs(fileFuture);
 		}
 	}
 
+	@Nested
+	class TestRespondWith {
+
+		private GrpcRouteForwardingResponse response = GrpcRouteForwardingResponse.newBuilder().build();
+		@Mock
+		private StreamObserver<GrpcRouteForwardingResponse> responseObserver;
+
+		@Test
+		void shouldCallOnNext() {
+			service.respondWith(responseObserver, response);
+
+			verify(responseObserver).onNext(response);
+		}
+
+		@Test
+		void shouldCallOnCompletedAfterOnNext() {
+			var inOrder = inOrder(responseObserver);
+
+			service.respondWith(responseObserver, response);
+
+			inOrder.verify(responseObserver).onNext(response);
+			inOrder.verify(responseObserver).onCompleted();
+		}
+	}
 }
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapperTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..406c6e1bd700ebde38acdbb237a49d4996685f3b
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingMapperTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+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.mapstruct.factory.Mappers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import de.ozgcloud.eingang.common.formdata.AntragstellerTestFactory;
+import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.common.formdata.ServiceKontoTestFactory;
+import de.ozgcloud.eingang.common.formdata.ZustaendigeStelle;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwarding;
+import de.ozgcloud.eingang.router.GrpcEingangHeaderTestFactory;
+import de.ozgcloud.eingang.router.ServiceKontoMapper;
+import de.ozgcloud.vorgang.common.grpc.GrpcFormDataMapper;
+
+class RouteForwardingMapperTest {
+
+	@InjectMocks
+	private RouteForwardingMapper mapper = Mappers.getMapper(RouteForwardingMapper.class);
+
+	@Mock
+	private GrpcFormDataMapper grpcFormDataMapper;
+	@Mock
+	private ServiceKontoMapper serviceKontoMapper;
+
+	@Nested
+	class TestToFormData {
+
+		private final GrpcRouteForwarding routeForwarding = GrpcRouteForwardingTestFactory.create();
+
+		@BeforeEach
+		void mock() {
+			when(serviceKontoMapper.fromGrpc(any())).thenReturn(ServiceKontoTestFactory.create());
+			when(grpcFormDataMapper.mapFromFormData(GrpcAntragstellerTestFactory.OTHER_DATA)).thenReturn(AntragstellerTestFactory.DATA);
+			when(grpcFormDataMapper.mapFromFormData(GrpcEingangStubTestFactory.FORM_DATA)).thenReturn(FormDataTestFactory.FORM_DATA);
+		}
+
+		@Test
+		void shouldCallServiceKontoMapper() {
+			mapper.toFormData(routeForwarding);
+
+			verify(serviceKontoMapper).fromGrpc(GrpcEingangHeaderTestFactory.SERVICE_KONTO);
+		}
+
+		@Test
+		void shouldCallFormDataMapperWithAntragstellerData() {
+			mapper.toFormData(routeForwarding);
+
+			verify(grpcFormDataMapper).mapFromFormData(GrpcAntragstellerTestFactory.OTHER_DATA);
+		}
+
+		@Test
+		void shouldCallFormDataMapperWithFormData() {
+			mapper.toFormData(routeForwarding);
+
+			verify(grpcFormDataMapper).mapFromFormData(GrpcEingangStubTestFactory.FORM_DATA);
+		}
+
+		@Test
+		void shouldMapHeader() {
+			var formDataHeader = mapper.toFormData(routeForwarding).getHeader();
+
+			assertThat(formDataHeader).usingRecursiveComparison().isEqualTo(FormDataTestFactory.HEADER);
+		}
+
+		@Test
+		void shouldMapAntragsteller() {
+			var expectedAntragSteller = AntragstellerTestFactory.createBuilder().firmaName(AntragstellerTestFactory.FIRMA_NAME).build();
+
+			var antragsteller = mapper.toFormData(routeForwarding).getAntragsteller();
+
+			assertThat(antragsteller).usingRecursiveComparison().isEqualTo(expectedAntragSteller);
+		}
+
+		@Test
+		void shouldMapFormData() {
+			var formData = mapper.toFormData(routeForwarding).getFormData();
+
+			assertThat(formData).isSameAs(FormDataTestFactory.FORM_DATA);
+		}
+
+		@Test
+		void shouldMapZustaendigeStelles() {
+			var expectedZustaendigeStelle = ZustaendigeStelle.builder()
+					.organisationseinheitenId(GrpcRouteCriteriaTestFactory.ORGANISATION_EINHEIT_ID)
+					.build();
+
+			var zustaendigeStelles = mapper.toFormData(routeForwarding).getZustaendigeStelles();
+
+			assertThat(zustaendigeStelles).usingRecursiveFieldByFieldElementComparator().containsExactly(expectedZustaendigeStelle);
+		}
+	}
+}
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingServiceTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b36715c71b92587c4522f669a8631a4d527b6c7
--- /dev/null
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingServiceTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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.forwarder;
+
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.router.VorgangService;
+
+class RouteForwardingServiceTest {
+
+	@InjectMocks
+	private RouteForwardingService routeForwardingService;
+	@Mock
+	private VorgangService vorgangService;
+
+	@Nested
+	class TestRoute {
+
+		private final FormData formData = FormDataTestFactory.create();
+
+		@Test
+		void shouldCallVorgangService() {
+			routeForwardingService.route(formData);
+
+			verify(vorgangService).createVorgang(formData);
+		}
+	}
+}
diff --git a/pom.xml b/pom.xml
index ce7c182f5e8169cd94182f41dd29ae3f3f1cfe06..4d92bb83ed28b5166aaaa4757bcd44bd8d5baa46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.9.0</version>
+		<version>4.11.0</version>
 		<relativePath /> <!-- lookup parent from repository -->
 	</parent>
 
@@ -45,6 +45,7 @@
 
 	<modules>
 		<module>common</module>
+		<module>eingang-manager-interface</module>
 		<module>router</module>
 		<module>forwarder</module>
 		<module>semantik-adapter</module>
@@ -54,7 +55,7 @@
 	</modules>
 
 	<properties>
-		<vorgang-manager.version>2.10.0</vorgang-manager.version>
+		<vorgang-manager.version>2.24.0-OZG-7573-forwarding-interface-SNAPSHOT</vorgang-manager.version>
 		<zufi-manager.version>1.7.0</zufi-manager.version>
 
 		<jsoup.version>1.14.3</jsoup.version>
@@ -75,11 +76,21 @@
 				<artifactId>common</artifactId>
 				<version>${project.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>de.ozgcloud.eingang</groupId>
+				<artifactId>eingang-manager-interface</artifactId>
+				<version>${project.version}</version>
+			</dependency>
 			<dependency>
 				<groupId>de.ozgcloud.eingang</groupId>
 				<artifactId>router</artifactId>
 				<version>${project.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>de.ozgcloud.eingang</groupId>
+				<artifactId>forwarder</artifactId>
+				<version>${project.version}</version>
+			</dependency>
 			<dependency>
 				<groupId>de.ozgcloud.eingang</groupId>
 				<artifactId>semantik-adapter</artifactId>
@@ -126,6 +137,13 @@
 				<scope>test</scope>
 				<version>${project.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>de.ozgcloud.eingang</groupId>
+				<artifactId>router</artifactId>
+				<version>${project.version}</version>
+				<type>test-jar</type>
+				<scope>test</scope>
+			</dependency>
 			<dependency>
 				<groupId>de.ozgcloud.vorgang</groupId>
 				<artifactId>vorgang-manager-utils</artifactId>
diff --git a/router/pom.xml b/router/pom.xml
index 7130d2c1e52b111ebd0b1600c9ad76e614fcb97b..f0659ace437ff2c4e26c281df46951dc54e7fb58 100644
--- a/router/pom.xml
+++ b/router/pom.xml
@@ -96,6 +96,18 @@
 
 	<build>
 		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<executions>
+					<execution>
+						<goals>
+							<goal>test-jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
 			<plugin>
 				<groupId>org.jacoco</groupId>
 				<artifactId>jacoco-maven-plugin</artifactId>
diff --git a/router/src/main/java/de/ozgcloud/eingang/common/zufi/XzufiId.java b/router/src/main/java/de/ozgcloud/eingang/common/zufi/XzufiId.java
index ff731658b7be8e9afbf001627923667b48ec49ab..bbcb44b94655bca0f778332dde1f395cf97587c3 100644
--- a/router/src/main/java/de/ozgcloud/eingang/common/zufi/XzufiId.java
+++ b/router/src/main/java/de/ozgcloud/eingang/common/zufi/XzufiId.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.common.zufi;
 
 import lombok.Builder;
diff --git a/router/src/main/java/de/ozgcloud/eingang/router/GrpcEingangMapper.java b/router/src/main/java/de/ozgcloud/eingang/router/GrpcEingangMapper.java
index eb5d7c414ef8361bfcc348e1f2544a563ce853a5..b5fd75f9ad35d3c57c5d506518c269011cf48815 100644
--- a/router/src/main/java/de/ozgcloud/eingang/router/GrpcEingangMapper.java
+++ b/router/src/main/java/de/ozgcloud/eingang/router/GrpcEingangMapper.java
@@ -44,7 +44,6 @@ import de.ozgcloud.vorgang.vorgang.GrpcIncomingFileGroup;
 import de.ozgcloud.vorgang.vorgang.GrpcZustaendigeStelle;
 
 @Mapper(unmappedTargetPolicy = ReportingPolicy.WARN, //
-		unmappedSourcePolicy = ReportingPolicy.WARN, //
 		nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, //
 		nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, //
 		collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, //
@@ -59,8 +58,8 @@ public interface GrpcEingangMapper {
 
 	default ZustaendigeStelle getZustaendigeStelle(FormData formData, Optional<String> organisationsEinheitenId) {
 		return organisationsEinheitenId.flatMap(oeId -> formData.getZustaendigeStelles().stream()
-						.filter(zustaendigeStelle -> zustaendigeStelle.getOrganisationseinheitenId().equals(oeId))
-						.findFirst())
+				.filter(zustaendigeStelle -> zustaendigeStelle.getOrganisationseinheitenId().equals(oeId))
+				.findFirst())
 				.orElseGet(() -> ZustaendigeStelle.builder().build());
 	}
 
@@ -83,14 +82,4 @@ public interface GrpcEingangMapper {
 	default String uuidToString(UUID id) {
 		return id.toString();
 	}
-
-	// FIXME map representations and attachments
-	@Mapping(target = "attachment", ignore = true)
-	@Mapping(target = "attachments", ignore = true)
-	@Mapping(target = "representation", ignore = true)
-	@Mapping(target = "representations", ignore = true)
-	// TOASK: Wird aktuell nicht gebraucht, trotzdem implementiern?
-	@Mapping(target = "header.serviceKonto", ignore = true)
-	FormData toFormData(GrpcEingang eingang);
-
 }
diff --git a/router/src/main/java/de/ozgcloud/eingang/router/ServiceKontoMapper.java b/router/src/main/java/de/ozgcloud/eingang/router/ServiceKontoMapper.java
index 7986140e79963d3d4183146800211926678fb748..97aed9ca47f8d3de4a560c577687186f56b4a7e3 100644
--- a/router/src/main/java/de/ozgcloud/eingang/router/ServiceKontoMapper.java
+++ b/router/src/main/java/de/ozgcloud/eingang/router/ServiceKontoMapper.java
@@ -23,55 +23,67 @@
  */
 package de.ozgcloud.eingang.router;
 
-import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.ReportingPolicy;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 
 import de.ozgcloud.eingang.common.formdata.PostfachAddressIdentifier;
 import de.ozgcloud.eingang.common.formdata.ServiceKonto;
 import de.ozgcloud.eingang.common.formdata.StringBasedIdentifier;
-import de.ozgcloud.eingang.common.formdata.ServiceKonto.PostfachAddress;
 import de.ozgcloud.vorgang.common.GrpcObject;
 import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper;
-import de.ozgcloud.vorgang.vorgang.GrpcPostfachAddress;
 import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto;
 
-@Component
-class ServiceKontoMapper {
+@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN, //
+		nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, //
+		nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, //
+		collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, //
+		uses = { GrpcObjectMapper.class })
+public abstract class ServiceKontoMapper {
 
 	static final String DEFAULT_TRUST_LEVEL = "STORK-QAA-Level-1";
 
 	@Autowired
 	private GrpcObjectMapper grpcObjectMapper;
 
-	public GrpcServiceKonto toServiceKonto(ServiceKonto serviceKonto) {
-		return GrpcServiceKonto.newBuilder()
-				.setType(serviceKonto.getType())
-				.addAllPostfachAddresses(getPostfachAddresses(serviceKonto))
-				.setTrustLevel(Optional.ofNullable(serviceKonto.getTrustLevel()).orElse(DEFAULT_TRUST_LEVEL))
-				.build();
-	}
+	@Mapping(target = "postfachAddressesList", source = "postfachAddresses")
+	@Mapping(target = "trustLevel", source = "trustLevel", defaultValue = DEFAULT_TRUST_LEVEL)
+	abstract GrpcServiceKonto toGrpc(ServiceKonto serviceKonto);
 
-	private List<GrpcPostfachAddress> getPostfachAddresses(ServiceKonto serviceKonto) {
-		return serviceKonto.getPostfachAddresses().stream().map(this::fromPostfachAddress).toList();
+	GrpcObject mapFromIdentifier(PostfachAddressIdentifier identifier) {
+		return grpcObjectMapper.fromMap(Map.of(StringBasedIdentifier.POSTFACH_ID_FIELD,
+				getStringBasedValue(identifier)));
 	}
 
-	private GrpcPostfachAddress fromPostfachAddress(PostfachAddress postfachAddress) {
-		return GrpcPostfachAddress.newBuilder()
-				.setVersion(postfachAddress.getVersion())
-				.setType(postfachAddress.getType())
-				.setIdentifier(mapFromIdentifier(postfachAddress.getIdentifier()))
-				.build();
+	String getStringBasedValue(PostfachAddressIdentifier identifier) {
+		return ((StringBasedIdentifier) identifier).getPostfachId();
 	}
 
-	GrpcObject mapFromIdentifier(PostfachAddressIdentifier identifier) {
-		return grpcObjectMapper.fromMap(Map.of(StringBasedIdentifier.POSTFACH_ID_FIELD, getStringBasedValue(identifier)));
+	@Mapping(target = "postfachAddresses", source = "postfachAddressesList")
+	@Mapping(target = "trustLevel", source = "trustLevel", qualifiedByName = "mapTrustLevel")
+	@Mapping(target = "postfachAddress", ignore = true)
+	public abstract ServiceKonto fromGrpc(GrpcServiceKonto serviceKonto);
+
+	PostfachAddressIdentifier mapToIdentifier(GrpcObject identifier) {
+		var stringIdentifier = MapUtils.getString(grpcObjectMapper.mapFromGrpc(identifier), StringBasedIdentifier.POSTFACH_ID_FIELD);
+		return StringBasedIdentifier.builder().postfachId(stringIdentifier).build();
 	}
 
-	private String getStringBasedValue(PostfachAddressIdentifier identifier) {
-		return ((StringBasedIdentifier) identifier).getPostfachId();
+	@Named("mapTrustLevel")
+	String mapTrustLevel(String trustLevel) {
+		if (StringUtils.isEmpty(trustLevel)) {
+			return DEFAULT_TRUST_LEVEL;
+		}
+		return trustLevel;
 	}
+
 }
\ No newline at end of file
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangHeaderTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangHeaderTestFactory.java
index e7fbc2a6de22ef4cd73d3719433326f517ac0e6a..1c2f14b48549cbf74fa2c97f2a1e2a5890dbde26 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangHeaderTestFactory.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangHeaderTestFactory.java
@@ -30,6 +30,8 @@ import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto;
 
 public class GrpcEingangHeaderTestFactory {
 
+	public static final GrpcServiceKonto SERVICE_KONTO = GrpcServiceKontoTestFactory.create();
+
 	public static GrpcEingangHeader create() {
 		return createBuilder().build();
 	}
@@ -42,7 +44,7 @@ public class GrpcEingangHeaderTestFactory {
 				.setFormEngineName(FORM_ENGINE_NAME)
 				.setFormName(FORM_NAME)
 				.setSender(SENDER)
-				.setServiceKonto(GrpcServiceKonto.newBuilder().build())
+				.setServiceKonto(SERVICE_KONTO)
 				.setVorgangNummer(VORGANG_NUMMER);
 	}
 }
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperITCase.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperITCase.java
index 55e1b3ef348ce7c2f4a0bb418ef36195d0d56276..758fcd0ef1e924d7161e08eae22ee38325b7da3c 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperITCase.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperITCase.java
@@ -29,7 +29,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -38,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest;
 
 import de.ozgcloud.eingang.common.formdata.AntragstellerTestFactory;
 import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
 import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory;
 import de.ozgcloud.eingang.common.formdata.ZustaendigeStelleTestFactory;
 import de.ozgcloud.vorgang.vorgang.GrpcEingang;
@@ -59,7 +59,9 @@ class GrpcEingangMapperITCase {
 
 			@Test
 			void antragstellerShouldBeMapped() {
-				var antragSteller = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID)).getAntragsteller();
+				var antragSteller = grpcEingangMapper
+						.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID))
+						.getAntragsteller();
 
 				assertThat(antragSteller.getPostfachId()).isEqualTo(AntragstellerTestFactory.POSTFACH_ID);
 				assertThat(antragSteller.getVorname()).isEqualTo(AntragstellerTestFactory.VORNAME);
@@ -68,7 +70,9 @@ class GrpcEingangMapperITCase {
 
 			@Test
 			void dataShouldBeMapped() {
-				var antragsteller = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID)).getAntragsteller();
+				var antragsteller = grpcEingangMapper
+						.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID))
+						.getAntragsteller();
 
 				assertThat(antragsteller.getOtherData().getFieldList()).hasSize(1);
 				assertThat(antragsteller.getOtherData().getField(0).getName()).isEqualTo(AntragstellerTestFactory.GEBIET_BEZEICHNUNG_KEY);
@@ -81,7 +85,9 @@ class GrpcEingangMapperITCase {
 		class TestZustaendigeStelle {
 			@Test
 			void eingangShouldHaveZustaendigeStelle() {
-				var zustaendigeStelle = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID)).getZustaendigeStelle();
+				var zustaendigeStelle = grpcEingangMapper
+						.toEingang(FormDataTestFactory.create(), Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID))
+						.getZustaendigeStelle();
 
 				assertThat(zustaendigeStelle).isNotNull();
 				assertThat(zustaendigeStelle.getOrganisationseinheitenId()).isEqualTo(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID);
@@ -102,34 +108,30 @@ class GrpcEingangMapperITCase {
 		@DisplayName("Test mapped Attachments")
 		class TestAttachments {
 
-			private GrpcEingang eingang;
-
-			@BeforeEach
-			void init() {
-
-				eingang = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.empty());
-			}
-
 			@Test
 			void validateNumberOfAttachments() {
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.empty());
 
-				assertThat(eingang.getNumberOfAttachments()).isEqualTo(2);
+				assertThat(eingang.getNumberOfAttachments()).isEqualTo(FormDataTestFactory.ATTACHMENTS.size());
 			}
 
 			@Test
 			void validateNumberOfAttachmentGroups() {
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.empty());
 
-				assertThat(eingang.getAttachmentsCount()).isEqualTo(2);
+				assertThat(eingang.getAttachmentsCount()).isEqualTo(FormDataTestFactory.ATTACHMENTS.size());
 			}
 
 			@Test
 			void validateGroup1AttachmentCount() {
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.empty());
 
 				assertThat(eingang.getAttachmentsList().get(0).getFilesCount()).isEqualTo(1);
 			}
 
 			@Test
 			void validateGroup1Attachment() {
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.create(), Optional.empty());
 
 				GrpcIncomingFile attachment = eingang.getAttachmentsList().get(0).getFilesList().get(0);
 
@@ -142,12 +144,18 @@ class GrpcEingangMapperITCase {
 
 			@Test
 			void validateGroup2AttachmentCount() {
+				var attachmentGroup2 = IncomingFileGroupTestFactory.createBuilder().name("attachmentGroup2").build();
+
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.createBuilder().attachment(attachmentGroup2).build(), Optional.empty());
 
 				assertThat(eingang.getAttachmentsList().get(1).getFilesCount()).isEqualTo(1);
 			}
 
 			@Test
 			void validateGroup2Attachment() {
+				var attachmentGroup2 = IncomingFileGroupTestFactory.createBuilder().name("attachmentGroup2").build();
+
+				var eingang = grpcEingangMapper.toEingang(FormDataTestFactory.createBuilder().attachment(attachmentGroup2).build(), Optional.empty());
 
 				GrpcIncomingFile attachment = eingang.getAttachmentsList().get(1).getFilesList().get(0);
 
@@ -187,7 +195,8 @@ class GrpcEingangMapperITCase {
 			void valueListShouldGenerateFields() {
 
 				GrpcEingang eingang = grpcEingangMapper
-						.toEingang(FormDataTestFactory.createBuilder().formData(Map.of("key", List.of("value1", "value2"))).build(), Optional.empty());
+						.toEingang(FormDataTestFactory.createBuilder().formData(Map.of("key", List.of("value1", "value2"))).build(),
+								Optional.empty());
 
 				assertThat(eingang.getFormData().getFieldCount()).isEqualTo(2);
 			}
@@ -197,7 +206,8 @@ class GrpcEingangMapperITCase {
 
 				GrpcEingang eingang = grpcEingangMapper
 						.toEingang(FormDataTestFactory.createBuilder()
-								.formData(Map.of("key-1", List.of(Map.of("sub_key", "value1"), Map.of("sub_key", "value2")))).build(), Optional.empty());
+								.formData(Map.of("key-1", List.of(Map.of("sub_key", "value1"), Map.of("sub_key", "value2")))).build(),
+								Optional.empty());
 
 				assertThat(eingang.getFormData().getFormCount()).isEqualTo(2);
 				assertThat(eingang.getFormData().getForm(0).getFieldCount()).isEqualTo(1);
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperTest.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperTest.java
index 15ee7c6b776beb4fb5a8ff3074b562b44fbf55fd..eed08a8959bfb6067b971d92fa12b88613dad87a 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperTest.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcEingangMapperTest.java
@@ -45,7 +45,6 @@ import de.ozgcloud.vorgang.common.grpc.GrpcFormDataMapper;
 import de.ozgcloud.vorgang.vorgang.GrpcAntragsteller;
 import de.ozgcloud.vorgang.vorgang.GrpcEingang;
 import de.ozgcloud.vorgang.vorgang.GrpcFormData;
-import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto;
 import de.ozgcloud.vorgang.vorgang.GrpcZustaendigeStelle;
 
 class GrpcEingangMapperTest {
@@ -109,7 +108,7 @@ class GrpcEingangMapperTest {
 		void mockMapperReturnValues() {
 			when(grpcFormDataMapper.mapToFormData(anyMap()))
 					.thenReturn(GrpcFormData.newBuilder().addField(GrpcFormFieldTestFactory.create()).build());
-			when(serviceKontoMapper.toServiceKonto(any())).thenReturn(GrpcServiceKonto.newBuilder().build());
+			when(serviceKontoMapper.toGrpc(any())).thenReturn(GrpcServiceKontoTestFactory.create());
 		}
 
 		@Nested
@@ -134,14 +133,14 @@ class GrpcEingangMapperTest {
 			void validateNumberOfAttachments() {
 				var eingang = toEingang();
 
-				assertThat(eingang.getNumberOfAttachments()).isEqualTo(2);
+				assertThat(eingang.getNumberOfAttachments()).isEqualTo(FormDataTestFactory.ATTACHMENTS.size());
 			}
 
 			@Test
 			void validateNumberOfAttachmentGroups() {
 				var eingang = toEingang();
 
-				assertThat(eingang.getAttachmentsCount()).isEqualTo(2);
+				assertThat(eingang.getAttachmentsCount()).isEqualTo(FormDataTestFactory.ATTACHMENTS.size());
 			}
 
 			@Test
@@ -162,25 +161,6 @@ class GrpcEingangMapperTest {
 				assertThat(attachment.getContentType()).isEqualTo(IncomingFileTestFactory.CONTENT_TYPE);
 				assertThat(attachment.getContent()).isEmpty();
 			}
-
-			@Test
-			void validateGroup2AttachmentCount() {
-				var eingang = toEingang();
-
-				assertThat(eingang.getAttachmentsList().get(1).getFilesCount()).isEqualTo(1);
-			}
-
-			@Test
-			void validateGroup2Attachment() {
-				var eingang = toEingang();
-
-				var attachment = eingang.getAttachmentsList().get(1).getFilesList().get(0);
-				assertThat(attachment.getId()).isEqualTo(IncomingFileTestFactory.ID);
-				assertThat(attachment.getVendorId()).isEqualTo(IncomingFileTestFactory.VENDOR_ID);
-				assertThat(attachment.getName()).isEqualTo(IncomingFileTestFactory.NAME);
-				assertThat(attachment.getContentType()).isEqualTo(IncomingFileTestFactory.CONTENT_TYPE);
-				assertThat(attachment.getContent()).isEmpty();
-			}
 		}
 
 		@Nested
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileGroupTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileGroupTestFactory.java
index 8ffea979664c5c311d1130f87208667986df15e9..e8082ab9164a882cbcd9e7c2546424699925addc 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileGroupTestFactory.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileGroupTestFactory.java
@@ -23,12 +23,13 @@
  */
 package de.ozgcloud.eingang.router;
 
+import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory;
 import de.ozgcloud.vorgang.vorgang.GrpcIncomingFile;
 import de.ozgcloud.vorgang.vorgang.GrpcIncomingFileGroup;
 
 public class GrpcIncomingFileGroupTestFactory {
 
-	public static final String NAME = "FileGroup1";
+	public static final String NAME = IncomingFileGroupTestFactory.NAME;
 	public static final GrpcIncomingFile FILE = GrpcIncomingFileTestFactory.create();
 
 	public static GrpcIncomingFileGroup create() {
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileTestFactory.java
index dc5899c75627723337f54ec388396f35d9fa9abe..f72d588d6a437fa106b73f166c667bc6c76b75a7 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileTestFactory.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcIncomingFileTestFactory.java
@@ -30,7 +30,7 @@ import de.ozgcloud.vorgang.vorgang.GrpcIncomingFile;
 
 public class GrpcIncomingFileTestFactory {
 
-	public static final String ID = IncomingFileTestFactory.ID.toString();
+	public static final String ID = IncomingFileTestFactory.ID;
 	public static final String VENDOR_ID = IncomingFileTestFactory.VENDOR_ID;
 	public static final String NAME = IncomingFileTestFactory.NAME;
 	public static final String CONTENT_TYPE = IncomingFileTestFactory.CONTENT_TYPE;
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcPostfachAddressTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcPostfachAddressTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..902a463e9e880b9460a9109ffc7fb3925b16dbd1
--- /dev/null
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcPostfachAddressTestFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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.router;
+
+import de.ozgcloud.eingang.common.formdata.PostfachAddressTestFactory;
+import de.ozgcloud.eingang.common.formdata.StringBasedIdentifier;
+import de.ozgcloud.vorgang.common.GrpcObject;
+import de.ozgcloud.vorgang.common.GrpcProperty;
+import de.ozgcloud.vorgang.vorgang.GrpcPostfachAddress;
+import de.ozgcloud.vorgang.vorgang.GrpcPostfachAddress.Builder;
+
+public class GrpcPostfachAddressTestFactory {
+
+	public static final String VERSION = PostfachAddressTestFactory.VERSION;
+	public static final int TYPE = PostfachAddressTestFactory.POSTFACH_ADDRESS_TYPE;
+	public static final GrpcProperty IDENTIFIER_PROPERTY = GrpcProperty.newBuilder()
+			.setName(StringBasedIdentifier.POSTFACH_ID_FIELD)
+			.addValue(PostfachAddressTestFactory.POSTFACH_ID)
+			.build();
+	public static final GrpcObject IDENTIFIER = GrpcObject.newBuilder()
+			.addProperty(IDENTIFIER_PROPERTY)
+			.build();
+
+	public static GrpcPostfachAddress create() {
+		return createBuilder().build();
+	}
+
+	private static Builder createBuilder() {
+		return GrpcPostfachAddress.newBuilder()
+				.setVersion(VERSION)
+				.setType(TYPE)
+				.setIdentifier(IDENTIFIER);
+	}
+
+}
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/GrpcServiceKontoTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/GrpcServiceKontoTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..d223b64c551bdb7225826ee35e16cc0b3edd5039
--- /dev/null
+++ b/router/src/test/java/de/ozgcloud/eingang/router/GrpcServiceKontoTestFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 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.router;
+
+import de.ozgcloud.eingang.common.formdata.ServiceKontoTestFactory;
+import de.ozgcloud.vorgang.vorgang.GrpcPostfachAddress;
+import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto;
+import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto.Builder;
+
+public class GrpcServiceKontoTestFactory {
+
+	public static final GrpcPostfachAddress POSTFACH_ADDRESS = GrpcPostfachAddressTestFactory.create();
+	public static final String TRUST_LEVEL = ServiceKontoTestFactory.TRUST_LEVEL;
+	public static final String TYPE = ServiceKontoTestFactory.TYPE;
+
+	public static GrpcServiceKonto create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcServiceKonto.newBuilder()
+				.setType(TYPE)
+				.setTrustLevel(TRUST_LEVEL)
+				.addPostfachAddresses(POSTFACH_ADDRESS);
+	}
+
+}
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/ServiceKontoMapperTest.java b/router/src/test/java/de/ozgcloud/eingang/router/ServiceKontoMapperTest.java
index 34cc0776f020dcf4d9d1a578b8b587d2d2b3c42c..f6389906f7b4f1dc95fd744ba75c341faba0d82a 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/ServiceKontoMapperTest.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/ServiceKontoMapperTest.java
@@ -27,10 +27,14 @@ import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+import java.util.Map;
+
+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.mapstruct.factory.Mappers;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
@@ -45,13 +49,12 @@ import de.ozgcloud.vorgang.vorgang.GrpcServiceKonto;
 class ServiceKontoMapperTest {
 
 	@InjectMocks
-	private ServiceKontoMapper mapper;
+	private final ServiceKontoMapper mapper = Mappers.getMapper(ServiceKontoMapper.class);
 	@Mock
 	private GrpcObjectMapper grpcObjectMapper;
 
 	@Nested
-	@DisplayName("To servicekonto")
-	class TestMapServiceKonto {
+	class TestToGrpc {
 
 		private final GrpcProperty grpcProperty = GrpcProperty.newBuilder()
 				.setName(StringBasedIdentifier.POSTFACH_ID_FIELD)
@@ -89,7 +92,7 @@ class ServiceKontoMapperTest {
 
 		@Test
 		void shouldContainsDefaultTrustLevel() {
-			var serviceKonto = mapper.toServiceKonto(ServiceKontoTestFactory.createBuilder().trustLevel(null).build());
+			var serviceKonto = mapper.toGrpc(ServiceKontoTestFactory.createBuilder().trustLevel(null).build());
 
 			assertThat(serviceKonto.getTrustLevel()).isEqualTo(ServiceKontoMapper.DEFAULT_TRUST_LEVEL);
 		}
@@ -139,7 +142,38 @@ class ServiceKontoMapperTest {
 		}
 
 		private GrpcServiceKonto getServiceKontoFromMappedEingang() {
-			return mapper.toServiceKonto(ServiceKontoTestFactory.create());
+			return mapper.toGrpc(ServiceKontoTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestFromGrpc {
+
+		@BeforeEach
+		void mockGrpcObjectMapper() {
+			when(grpcObjectMapper.mapFromGrpc(any()))
+					.thenReturn(Map.of(StringBasedIdentifier.POSTFACH_ID_FIELD, PostfachAddressTestFactory.POSTFACH_ID));
+		}
+
+		@Test
+		void shouldCallGrpcMapper() {
+			mapper.fromGrpc(GrpcServiceKontoTestFactory.create());
+
+			verify(grpcObjectMapper).mapFromGrpc(GrpcPostfachAddressTestFactory.IDENTIFIER);
+		}
+
+		@Test
+		void shouldMapToGrpc() {
+			var serviceKonto = mapper.fromGrpc(GrpcServiceKontoTestFactory.create());
+
+			assertThat(serviceKonto).usingRecursiveComparison().isEqualTo(ServiceKontoTestFactory.create());
+		}
+
+		@Test
+		void shouldSetDefaultTrustLevel() {
+			var serviceKonto = mapper.fromGrpc(GrpcServiceKontoTestFactory.createBuilder().setTrustLevel(StringUtils.EMPTY).build());
+
+			assertThat(serviceKonto.getTrustLevel()).isEqualTo(ServiceKontoMapper.DEFAULT_TRUST_LEVEL);
 		}
 	}
 }
\ No newline at end of file
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java
index 7fda033b4b16edb312f09bc90f213392b3058a16..cc8cef24be0ae7c43b0a43fa6d80b9bd67e78c71 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java
@@ -342,7 +342,7 @@ class VorgangRemoteServiceTest {
 			void shouldCallUploadIncomingFile() {
 				vorgangCreator.uploadAttachments();
 
-				verify(vorgangCreator, times(2)).uploadIncomingFile(any(IncomingFile.class));
+				verify(vorgangCreator, times(FormDataTestFactory.ATTACHMENTS.size())).uploadIncomingFile(any(IncomingFile.class));
 			}
 
 			@Test
diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangServiceTest.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangServiceTest.java
index d69b36c664c87f2ad0116cf5e8f2e033a1358ec2..bd5ff001ac043671b3760e34a6921ac5c71b844e 100644
--- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangServiceTest.java
+++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangServiceTest.java
@@ -27,6 +27,8 @@ import static de.ozgcloud.eingang.common.formdata.FormDataTestFactory.*;
 import static java.util.Collections.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.*;
 
 import java.io.IOException;
@@ -152,7 +154,7 @@ class VorgangServiceTest {
 		void shouldReturnAttachmentsAndRepresentations() {
 			var files = service.getFormDataFiles(formData).toList();
 
-			assertThat(files).hasSize(3);
+			assertThat(files).hasSize(FormDataTestFactory.ATTACHMENTS.size() + FormDataTestFactory.REPRESENTATIONS.size());
 		}
 	}
 
diff --git a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/common/FormDataTestFactory.java b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/common/FormDataTestFactory.java
index 8576eb31d22093b5599b44b2381cf960eff2956b..594048a728e4da6dab7cc83fad469a2d5844d47a 100644
--- a/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/common/FormDataTestFactory.java
+++ b/semantik-adapter/src/test/java/de/ozgcloud/eingang/semantik/common/FormDataTestFactory.java
@@ -1,3 +1,26 @@
+/*
+ * Copyright (C) 2025 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.semantik.common;
 
 import java.util.HashMap;
@@ -5,10 +28,11 @@ import java.util.List;
 import java.util.Map;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
+import de.ozgcloud.eingang.common.formdata.PostfachAddressTestFactory;
 
 public class FormDataTestFactory {
 	public static final String POSTFACH_NAME_ID = "name-id-value";
-	public static final int REST_RESPONSE_NAME_MEMBER_SCOPE_MAILBOX_TYPE_VALUE = 1;
+	public static final int REST_RESPONSE_NAME_MEMBER_SCOPE_MAILBOX_TYPE_VALUE = PostfachAddressTestFactory.POSTFACH_ADDRESS_TYPE;
 
 	public static FormData create() {
 		return createBuilder().build();
diff --git a/src/main/helm/templates/network_policy.yaml b/src/main/helm/templates/network_policy.yaml
index 17f7bc782764722ca058e3261575128f7906d50b..b4bd33ed128b4dd3fc2a5283de4c58018bfaecfa 100644
--- a/src/main/helm/templates/network_policy.yaml
+++ b/src/main/helm/templates/network_policy.yaml
@@ -22,12 +22,12 @@
 # unter der Lizenz sind dem Lizenztext zu entnehmen.
 #
 
-{{- if not (.Values.networkPolicy).disabled }} 
+{{- if not (.Values.networkPolicy).disabled }}
 apiVersion: networking.k8s.io/v1
 kind: NetworkPolicy
 metadata:
   name: network-policy-{{ .Release.Name}}
-  namespace: {{ .Release.Namespace }} 
+  namespace: {{ .Release.Namespace }}
 spec:
   podSelector:
     matchLabels:
@@ -45,9 +45,19 @@ spec:
     ports:
     - protocol: TCP
       port: 8081
+  {{- if (.Values.forwarding).enabled }}
+  - ports:
+    - port: 9090
+      protocol: TCP
+    from:
+    - namespaceSelector: {}
+      podSelector:
+        matchLabels:
+          component: vorgang-manager
+  {{- end }}
   egress:
   - to:
-    - podSelector: 
+    - podSelector:
         matchLabels:
           component: vorgang-manager
     ports:
@@ -72,7 +82,7 @@ spec:
     - namespaceSelector:
         matchLabels:
           kubernetes.io/metadata.name: {{ required "routing.zufiManager.namespace must be set if routingStrategy=ZUFI" ((.Values.routing).zufiManager).namespace }}
-      podSelector: 
+      podSelector:
         matchLabels:
           component: zufi-server
     ports:
@@ -80,7 +90,7 @@ spec:
         protocol: TCP
   - to:
     - namespaceSelector: {}
-      podSelector: 
+      podSelector:
         matchLabels:
           component: vorgang-manager
     ports:
@@ -88,4 +98,4 @@ spec:
         protocol: TCP
 {{- end }}
 
-{{- end }} 
\ No newline at end of file
+{{- end }}
\ No newline at end of file
diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml
index d10537996010737c734c64c2cf2ffaecad92b3e8..be4c627e54167089a6137125149b2dcd964976e5 100644
--- a/src/test/helm/network_policy_test.yaml
+++ b/src/test/helm/network_policy_test.yaml
@@ -222,3 +222,42 @@ tests:
             ports:
               - port: 9090
                 protocol: TCP
+
+  - it: should add ingress from vorgang-manager if forwarding is enabled
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-server-namespace
+      forwarding:
+        enabled: true
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+              - namespaceSelector: {}
+                podSelector:
+                  matchLabels:
+                    component: vorgang-manager
+            ports:
+              - port: 9090
+                protocol: TCP
+
+  - it: should not add ingress from vorgang-manager if forwarding is disabled
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-server-namespace
+      forwarding:
+        enabled: false
+    asserts:
+      - notContains:
+          path: spec.ingress
+          content:
+            from:
+              - namespaceSelector: {}
+                podSelector:
+                  matchLabels:
+                    component: vorgang-manager
+            ports:
+              - port: 9090
+                protocol: TCP
+          any: true