diff --git a/vorgang-manager-server/pom.xml b/vorgang-manager-server/pom.xml
index b25865e7759f22793d02245ce707ba72cd06a375..30ca3125872e9e3f1e4ac0e3263ffdffa71792db 100644
--- a/vorgang-manager-server/pom.xml
+++ b/vorgang-manager-server/pom.xml
@@ -32,7 +32,7 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.11.0</version>
+		<version>4.12.0-OZG-7573-GrpcUpload-utils-SNAPSHOT</version>
 		<relativePath />
 	</parent>
 
@@ -51,7 +51,7 @@
 		<spring-boot.build-image.imageName>docker.ozg-sh.de/vorgang-manager:build-latest</spring-boot.build-image.imageName>
 
 		<zufi-manager-interface.version>1.6.0</zufi-manager-interface.version>
-
+		<common-lib.version>4.12.0-OZG-7573-GrpcUpload-utils-SNAPSHOT</common-lib.version>
 		<user-manager-interface.version>2.12.0</user-manager-interface.version>
 		<processor-manager.version>0.5.0</processor-manager.version>
 		<nachrichten-manager.version>2.18.0</nachrichten-manager.version>
@@ -60,7 +60,7 @@
 		<collaboration-manager.version>0.7.0</collaboration-manager.version>
 		<archive-manager.version>0.3.0</archive-manager.version>
 		<document-manager.version>1.2.0</document-manager.version>
-		<eingang-manager-interface.version>2.19.0-SNAPSHOT</eingang-manager-interface.version>
+		<eingang-manager-interface.version>2.19.0-OZG-7573-forward-vorgang-SNAPSHOT</eingang-manager-interface.version>
 
 		<zip.version>2.11.5</zip.version>
 		<jsoup.version>1.15.3</jsoup.version>
@@ -82,6 +82,11 @@
 			<version>${project.version}</version>
 		</dependency>
 
+		<dependency>
+			<groupId>de.ozgcloud.common</groupId>
+			<artifactId>ozgcloud-common-lib</artifactId>
+			<version>${common-lib.version}</version>
+		</dependency>
 		<dependency>
 			<groupId>de.ozgcloud.nachrichten</groupId>
 			<artifactId>nachrichten-manager-server</artifactId>
@@ -402,7 +407,8 @@
 						<env>
 							<BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
 							<BPE_APPEND_JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF-8</BPE_APPEND_JAVA_TOOL_OPTIONS>
-							<BPE_APPEND_JAVA_TOOL_OPTIONS>-Dio.grpc.netty.shaded.io.netty.maxDirectMemory=0</BPE_APPEND_JAVA_TOOL_OPTIONS>
+							<BPE_APPEND_JAVA_TOOL_OPTIONS>
+								-Dio.grpc.netty.shaded.io.netty.maxDirectMemory=0</BPE_APPEND_JAVA_TOOL_OPTIONS>
 							<BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:MaxDirectMemorySize=512m</BPE_APPEND_JAVA_TOOL_OPTIONS>
 							<BPE_APPEND_LC_ALL>en_US.UTF-8</BPE_APPEND_LC_ALL>
 						</env>
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapper.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapper.java
index aefc6d34ed1a5bc24bb6e1c2e3f48062b61da4e1..2e8f8d7ca9e5f6fccc295fae05c472d29ba3a392 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapper.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapper.java
@@ -24,9 +24,11 @@
 package de.ozgcloud.vorgang.vorgang;
 
 import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
 
 import com.google.protobuf.ByteString;
 
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile;
 import de.ozgcloud.vorgang.files.FileIdMapper;
 
 @Mapper(uses = FileIdMapper.class)
@@ -37,4 +39,17 @@ public interface IncomingFileMapper {
 	default byte[] map(ByteString value) {
 		return value.toByteArray();
 	}
+
+	@Mapping(target = "mergeFrom", ignore = true)
+	@Mapping(target = "clearField", ignore = true)
+	@Mapping(target = "clearOneof", ignore = true)
+	@Mapping(target = "mergeUnknownFields", ignore = true)
+	@Mapping(target = "contentTypeBytes", ignore = true)
+	@Mapping(target = "fileNameBytes", ignore = true)
+	@Mapping(target = "groupNameBytes", ignore = true)
+	@Mapping(target = "unknownFields", ignore = true)
+	@Mapping(target = "vendorIdBytes", ignore = true)
+	@Mapping(target = "allFields", ignore = true)
+	@Mapping(target = "fileName", source = "file.name")
+	GrpcAttachmentFile toAttachmentFile(String groupName, IncomingFile file);
 }
\ No newline at end of file
diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java
index 416b80956ca9e199dc23580b94c159db1097ff8d..c615f1720ff76b9f71cc433e7158822dc606bd10 100644
--- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java
+++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteService.java
@@ -23,18 +23,35 @@
  */
 package de.ozgcloud.vorgang.vorgang.redirect;
 
+import java.io.InputStream;
+import java.util.List;
 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 org.springframework.stereotype.Service;
 
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils;
+import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils.FileSender;
 import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.eingang.forwarder.RouteForwardingServiceGrpc;
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
+import de.ozgcloud.eingang.forwarding.GrpcFileContent;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
 import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor;
+import de.ozgcloud.vorgang.files.FileService;
 import de.ozgcloud.vorgang.vorgang.Eingang;
+import de.ozgcloud.vorgang.vorgang.IncomingFile;
+import de.ozgcloud.vorgang.vorgang.IncomingFileGroup;
+import de.ozgcloud.vorgang.vorgang.IncomingFileMapper;
 import de.ozgcloud.vorgang.vorgang.VorgangService;
+import io.grpc.stub.CallStreamObserver;
 import io.grpc.stub.StreamObserver;
 import lombok.RequiredArgsConstructor;
 import net.devh.boot.grpc.client.inject.GrpcClient;
@@ -43,10 +60,13 @@ import net.devh.boot.grpc.client.inject.GrpcClient;
 @RequiredArgsConstructor
 class ForwardingRemoteService {
 
+	private static final int TIMEOUT_MINUTES = 2;
 	private final VorgangService vorgangService;
 	private final ForwardingRequestMapper forwardingRequestMapper;
 	@GrpcClient("forwarder")
 	private final RouteForwardingServiceGrpc.RouteForwardingServiceStub serviceStub;
+	private final FileService fileService;
+	private final IncomingFileMapper incomingFileMapper;
 
 	public void forward(ForwardingRequest request) {
 		CompletableFuture<Void> responseFuture = new CompletableFuture<>();
@@ -69,7 +89,7 @@ class ForwardingRemoteService {
 	void sendEingang(ForwardingRequest request, StreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) {
 		var eingang = vorgangService.getById(request.getVorgangId()).getEingangs().getFirst();
 		requestStreamObserver.onNext(buildRouteForwardingRequest(request, eingang));
-
+		sendAttachments(eingang.getAttachments(), requestStreamObserver);
 	}
 
 	GrpcRouteForwardingRequest buildRouteForwardingRequest(ForwardingRequest request, Eingang eingang) {
@@ -77,6 +97,71 @@ class ForwardingRemoteService {
 		return GrpcRouteForwardingRequest.newBuilder().setRouteForwarding(routeForwarding).build();
 	}
 
+	void sendAttachments(List<IncomingFileGroup> attachments, StreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) {
+		for (var attachment : attachments) {
+			var groupName = attachment.getName();
+			attachment.getFiles().forEach(file -> sendAttachmentFile(requestStreamObserver, groupName, file));
+		}
+	}
+
+	private void sendAttachmentFile(StreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, String groupName, IncomingFile file) {
+		var fileContentStream = fileService.getUploadedFileStream(file.getId());
+		var fileSender = createFileSender(requestStreamObserver, groupName, file, fileContentStream).send();
+		waitForFinishedFileUpload(fileSender, fileContentStream);
+	}
+
+	FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createFileSender(
+			StreamObserver<GrpcRouteForwardingRequest> requestStreamObserver, String groupName, IncomingFile file, InputStream fileContentStream) {
+		return GrpcFileUploadUtils
+				.createSender(this::buildAttachmentChunk, fileContentStream, requestCallStreamObserverProvider(requestStreamObserver), false)
+				.withMetaData(buildGrpcAttachmentFile(groupName, file));
+	}
+
+	private Function<StreamObserver<GrpcRouteForwardingResponse>, CallStreamObserver<GrpcRouteForwardingRequest>> requestCallStreamObserverProvider(
+			StreamObserver<GrpcRouteForwardingRequest> requestStreamObserver) {
+		return response -> (CallStreamObserver<GrpcRouteForwardingRequest>) requestStreamObserver;
+	}
+
+	GrpcRouteForwardingRequest buildAttachmentChunk(byte[] chunk, int length) {
+		var fileContentBuilder = GrpcFileContent.newBuilder();
+		if (length <= 0) {
+			fileContentBuilder.setIsEndOfFile(true);
+		} else {
+			fileContentBuilder.setContent(ByteString.copyFrom(chunk));
+		}
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setAttachment(GrpcAttachment.newBuilder()
+						.setContent(fileContentBuilder)
+						.build())
+				.build();
+	}
+
+	GrpcRouteForwardingRequest buildGrpcAttachmentFile(String name, IncomingFile file) {
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setAttachment(GrpcAttachment.newBuilder()
+						.setFile(incomingFileMapper.toAttachmentFile(name, file))
+						.build())
+				.build();
+	}
+
+	void waitForFinishedFileUpload(FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender, InputStream inputStream) {
+		try {
+			fileSender.getResultFuture().get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			fileSender.cancelOnError(e);
+			throw new TechnicalException("Waiting for finishing file upload was interrupted.", e);
+		} catch (ExecutionException e) {
+			fileSender.cancelOnError(e);
+			throw new TechnicalException("Error on uploading file content.", e);
+		} catch (TimeoutException e) {
+			fileSender.cancelOnTimeout();
+			throw new TechnicalException("Timeout on uploading file content.", e);
+		} finally {
+			IOUtils.closeQuietly(inputStream);
+		}
+	}
+
 	private static void waitForCompletion(CompletableFuture<Void> responseFuture) {
 		try {
 			responseFuture.get();
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/EingangTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/EingangTestFactory.java
index 9fa8813dda465272e1279e1015337fe1c15efd45..b12ea789aec30333f8db2dbec36319945a429be7 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/EingangTestFactory.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/EingangTestFactory.java
@@ -49,6 +49,8 @@ public class EingangTestFactory {
 	public static final Map<String, Object> SUBFORM_FLAT = Map.of(SUBFORM_FIELD_NAME, SUBFORM_FIELD);
 	public static final Map<String, Object> FORM_DATA = Map.of(SINGLE_FIELD_NAME, SINGLE_FIELD, SUBFORM_NAME, SUBFORM);
 
+	public static final IncomingFileGroup ATTACHMENT = IncomingFileGroupTestFactory.create();
+
 	public static Eingang create() {
 		return createBuilder().build();
 	}
@@ -59,7 +61,7 @@ public class EingangTestFactory {
 				.header(EingangHeaderTestFactory.create())
 				.antragsteller(AntragstellerTestFactory.create())
 				.formData(FORM_DATA)
-				.attachment(IncomingFileGroupTestFactory.create())
+				.attachment(ATTACHMENT)
 				.representation(IncomingFileTestFactory.create())
 				.zustaendigeStelle(ZustaendigeStelleTestFactory.create());
 	}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapperTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapperTest.java
index e59250ca0f7f5a57efec908af88708184b7f1d6e..d6f41ddfe971f01da392390612cdf264ff5680b8 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapperTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/IncomingFileMapperTest.java
@@ -32,6 +32,7 @@ import org.mockito.InjectMocks;
 import org.mockito.Spy;
 
 import de.ozgcloud.vorgang.files.FileIdMapper;
+import de.ozgcloud.vorgang.vorgang.redirect.GrpcAttachmentFileTestFactory;
 
 class IncomingFileMapperTest {
 
@@ -50,4 +51,15 @@ class IncomingFileMapperTest {
 			assertThat(result).isNotNull().usingRecursiveComparison().isEqualTo(IncomingFileTestFactory.create());
 		}
 	}
+
+	@Nested
+	class TestToAttachmentFile {
+
+		@Test
+		void shouldMapToGrpcAttachmentFile() {
+			var result = mapper.toAttachmentFile(GrpcAttachmentFileTestFactory.GROUP_NAME, IncomingFileTestFactory.create());
+
+			assertThat(result).isEqualTo(GrpcAttachmentFileTestFactory.create());
+		}
+	}
 }
\ No newline at end of file
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java
index df39403250bbf6ef883f1797174e92d1897fbc26..ccebcdf9118535c115a0a1ffc033066c7b5d5f69 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/ForwardingRemoteServiceTest.java
@@ -24,34 +24,67 @@
 package de.ozgcloud.vorgang.vorgang.redirect;
 
 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.InputStream;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Spy;
 
+import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils;
+import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils.FileSender;
+import de.ozgcloud.common.errorhandling.TechnicalException;
 import de.ozgcloud.eingang.forwarder.RouteForwardingServiceGrpc;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
 import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor;
+import de.ozgcloud.vorgang.files.FileService;
+import de.ozgcloud.vorgang.vorgang.EingangTestFactory;
+import de.ozgcloud.vorgang.vorgang.IncomingFile;
+import de.ozgcloud.vorgang.vorgang.IncomingFileGroup;
+import de.ozgcloud.vorgang.vorgang.IncomingFileGroupTestFactory;
+import de.ozgcloud.vorgang.vorgang.IncomingFileMapper;
+import de.ozgcloud.vorgang.vorgang.IncomingFileTestFactory;
 import de.ozgcloud.vorgang.vorgang.VorgangService;
 import de.ozgcloud.vorgang.vorgang.VorgangTestFactory;
 import de.ozgcloud.vorgang.vorgang.redirect.ForwardingRemoteService.ForwardingResponseObserver;
+import io.grpc.stub.CallStreamObserver;
 import io.grpc.stub.StreamObserver;
+import lombok.SneakyThrows;
 
 class ForwardingRemoteServiceTest {
 
+	@Spy
+	@InjectMocks
+	private ForwardingRemoteService service;
 	@Mock
 	private VorgangService vorgangService;
 	@Mock
 	private ForwardingRemoteService forwardingRemoteService;
 	@Mock
 	private RouteForwardingServiceGrpc.RouteForwardingServiceStub serviceStub;
-	@Spy
-	@InjectMocks
-	private ForwardingRemoteService service;
+	@Mock
+	private FileService fileService;
+	@Mock
+	private IncomingFileMapper incomingFileMapper;
 
 	@Mock
 	private StreamObserver<GrpcRouteForwardingRequest> requestObserver;
@@ -160,6 +193,7 @@ class ForwardingRemoteServiceTest {
 		void init() {
 			when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create());
 			doReturn(GrpcRouteForwardingRequestTestFactory.create()).when(service).buildRouteForwardingRequest(any(), any());
+			doNothing().when(service).sendAttachments(any(), any());
 		}
 
 		@Test
@@ -169,8 +203,392 @@ class ForwardingRemoteServiceTest {
 			verify(vorgangService).getById(VorgangTestFactory.ID);
 		}
 
+		@Test
+		void shouldCallSendAttachments() {
+			sendEingang();
+
+			verify(service).sendAttachments(List.of(EingangTestFactory.ATTACHMENT), requestObserver);
+		}
+
 		private void sendEingang() {
 			service.sendEingang(request, requestObserver);
 		}
 	}
+
+	@Nested
+	class TestSendAttachments {
+
+		@Mock
+		private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender;
+		@Mock
+		private InputStream inputStream;
+
+		private final IncomingFileGroup attachment = IncomingFileGroupTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			when(fileService.getUploadedFileStream(any())).thenReturn(inputStream);
+			doReturn(fileSender).when(service).createFileSender(any(), any(), any(), any());
+			when(fileSender.send()).thenReturn(fileSender);
+		}
+
+		@Test
+		void shouldGetUploadedFileContent() {
+			sendAttachments();
+
+			verify(fileService).getUploadedFileStream(IncomingFileTestFactory.ID);
+		}
+
+		@Test
+		void shouldCallCreateFileSender() {
+			sendAttachments();
+
+			verify(service).createFileSender(requestObserver, IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE,
+					inputStream);
+		}
+
+		@Test
+		void shouldSend() {
+			sendAttachments();
+
+			verify(fileSender).send();
+		}
+
+		@Test
+		void shouldCallWaitForFinishedFileUploadAfterSend() {
+			var inOrder = inOrder(fileSender, service);
+
+			sendAttachments();
+
+			inOrder.verify(fileSender).send();
+			inOrder.verify(service).waitForFinishedFileUpload(fileSender, inputStream);
+		}
+
+		private void sendAttachments() {
+			service.sendAttachments(List.of(attachment), requestObserver);
+		}
+	}
+
+	@Nested
+	class TestCreateFileSender {
+
+		private MockedStatic<GrpcFileUploadUtils> grpcFileUploadUtilsMock;
+		@Mock
+		private CallStreamObserver<GrpcRouteForwardingRequest> requestCallStreamObserver;
+		@Mock
+		private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender;
+		@Mock
+		private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSenderWithMetadata;
+		@Mock
+		private StreamObserver<GrpcRouteForwardingResponse> responseObserver;
+		@Mock
+		private InputStream inputStream;
+		@Captor
+		private ArgumentCaptor<BiFunction<byte[], Integer, GrpcRouteForwardingRequest>> chunkBuilderCaptor;
+		@Captor
+		private ArgumentCaptor<Function<StreamObserver<GrpcRouteForwardingResponse>, CallStreamObserver<GrpcRouteForwardingRequest>>> reqObserverBuilderCaptor;
+
+		private final byte[] chunk = RandomUtils.insecure().randomBytes(5);
+		private final GrpcRouteForwardingRequest metadataRequest = GrpcRouteForwardingRequestTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			grpcFileUploadUtilsMock = mockStatic(GrpcFileUploadUtils.class);
+			grpcFileUploadUtilsMock.when(() -> GrpcFileUploadUtils.createSender(any(), any(), any(), anyBoolean())).thenReturn(fileSender);
+			doReturn(metadataRequest).when(service).buildGrpcAttachmentFile(any(), any());
+			when(fileSender.withMetaData(any())).thenReturn(fileSenderWithMetadata);
+		}
+
+		@AfterEach
+		void tearDown() {
+			grpcFileUploadUtilsMock.close();
+		}
+
+		@Test
+		void shouldCreateFileSenderWithChunkBuilder() {
+			createFileSender();
+
+			grpcFileUploadUtilsMock.verify(() -> GrpcFileUploadUtils.createSender(chunkBuilderCaptor.capture(), any(), any(), anyBoolean()));
+			chunkBuilderCaptor.getValue().apply(chunk, chunk.length);
+			verify(service).buildAttachmentChunk(chunk, chunk.length);
+		}
+
+		@Test
+		void shouldCreateFileSenderWithInputStream() {
+			createFileSender();
+
+			grpcFileUploadUtilsMock.verify(() -> GrpcFileUploadUtils.createSender(any(), eq(inputStream), any(), anyBoolean()));
+		}
+
+		@Test
+		void shouldCreateFileSenderWithRequestObserverBuilder() {
+			createFileSender();
+
+			grpcFileUploadUtilsMock.verify(() -> GrpcFileUploadUtils.createSender(any(), any(), reqObserverBuilderCaptor.capture(), anyBoolean()));
+			assertThat(reqObserverBuilderCaptor.getValue().apply(responseObserver)).isSameAs(requestCallStreamObserver);
+		}
+
+		@Test
+		void shouldCreateFileSenderWithNotCompleteOnFileSent() {
+			createFileSender();
+
+			grpcFileUploadUtilsMock.verify(() -> GrpcFileUploadUtils.createSender(any(), any(), any(), eq(false)));
+		}
+
+		@Test
+		void shouldCallBuildGrpcAttachmentFile() {
+			createFileSender();
+
+			verify(service).buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE);
+		}
+
+		@Test
+		void shouldSetMetaData() {
+			createFileSender();
+
+			verify(fileSender).withMetaData(metadataRequest);
+		}
+
+		@Test
+		void shouldReturnBuiltFileSender() {
+			var returnedFileSender = createFileSender();
+
+			assertThat(returnedFileSender).isSameAs(fileSenderWithMetadata);
+		}
+
+		private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> createFileSender() {
+			return service.createFileSender(requestCallStreamObserver, IncomingFileGroupTestFactory.NAME, IncomingFileGroupTestFactory.FILE,
+					inputStream);
+		}
+	}
+
+	@Nested
+	class TestBuildAttachmentChunk {
+
+		@Nested
+		class TestOnEndOfFile {
+
+			@Test
+			void shouldBuildEndOfFileChunk() {
+				var fileContent = service.buildAttachmentChunk(new byte[0], -1).getAttachment().getContent();
+
+				assertThat(fileContent).isEqualTo(GrpcFileContentTestFactory.createEndOfFile());
+			}
+		}
+
+		@Nested
+		class TestOnContentProvided {
+
+			@Test
+			void shouldBuildEndOfFileChunk() {
+				var fileContent = service.buildAttachmentChunk(GrpcFileContentTestFactory.CONTENT, GrpcFileContentTestFactory.CONTENT.length)
+						.getAttachment().getContent();
+
+				assertThat(fileContent).isEqualTo(GrpcFileContentTestFactory.create());
+			}
+		}
+	}
+
+	@Nested
+	class TestBuildGrpcAttachmentFile {
+
+		private IncomingFile file = IncomingFileTestFactory.create();
+
+		@BeforeEach
+		void mock() {
+			when(incomingFileMapper.toAttachmentFile(any(), any())).thenReturn(GrpcAttachmentFileTestFactory.create());
+		}
+
+		@Test
+		void shouldCallIncomingFileMapper() {
+			service.buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, file);
+
+			verify(incomingFileMapper).toAttachmentFile(IncomingFileGroupTestFactory.NAME, file);
+		}
+
+		@Test
+		void shoudlReturnAttachmentMetadataRequest() {
+			var result = service.buildGrpcAttachmentFile(IncomingFileGroupTestFactory.NAME, file);
+
+			assertThat(result).isEqualTo(GrpcRouteForwardingRequestTestFactory.createWithAttachmentMetadata());
+		}
+	}
+
+	@Nested
+	class TestWaitForFinishedFileUpload {
+
+		@Mock
+		private FileSender<GrpcRouteForwardingRequest, GrpcRouteForwardingResponse> fileSender;
+		@Mock
+		private InputStream inputStream;
+		@Mock
+		private CompletableFuture<GrpcRouteForwardingResponse> resultFuture;
+
+		@BeforeEach
+		void mock() {
+			when(fileSender.getResultFuture()).thenReturn(resultFuture);
+		}
+
+		@Test
+		void shouldGetResultFuture() {
+			waitForFinishedFileUpload();
+
+			verify(fileSender).getResultFuture();
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldGetResultFromFuture() {
+			waitForFinishedFileUpload();
+
+			verify(resultFuture).get(2, TimeUnit.MINUTES);
+		}
+
+		@Test
+		@SneakyThrows
+		void shouldCloseInputStream() {
+			try {
+				waitForFinishedFileUpload();
+			} catch (TechnicalException e) {
+				// expected
+			}
+
+			verify(inputStream).close();
+		}
+
+		@Nested
+		class TestOnInterruptedException {
+
+			private InterruptedException exception = new InterruptedException();
+
+			@BeforeEach
+			@SneakyThrows
+			void mock() {
+				when(resultFuture.get(anyLong(), any())).thenThrow(exception);
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> waitForFinishedFileUpload());
+			}
+
+			@Test
+			void shouldInterruptThread() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				assertThat(Thread.currentThread().isInterrupted()).isTrue();
+			}
+
+			@Test
+			void shouldCancelOnError() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(fileSender).cancelOnError(exception);
+			}
+
+			@Test
+			@SneakyThrows
+			void shouldCloseInputStream() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(inputStream).close();
+			}
+		}
+
+		@Nested
+		class TestOnExecutionException {
+
+			private ExecutionException exception = new ExecutionException(new Exception());
+
+			@BeforeEach
+			@SneakyThrows
+			void mock() {
+				when(resultFuture.get(anyLong(), any())).thenThrow(exception);
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> waitForFinishedFileUpload());
+			}
+
+			@Test
+			void shouldCancelOnError() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(fileSender).cancelOnError(exception);
+			}
+
+			@Test
+			@SneakyThrows
+			void shouldCloseInputStream() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(inputStream).close();
+			}
+		}
+
+		@Nested
+		class TestOnTimeoutException {
+
+			private TimeoutException exception = new TimeoutException();
+
+			@BeforeEach
+			@SneakyThrows
+			void mock() {
+				when(resultFuture.get(anyLong(), any())).thenThrow(exception);
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThrows(TechnicalException.class, () -> waitForFinishedFileUpload());
+			}
+
+			@Test
+			void shouldCancelOnTimeout() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(fileSender).cancelOnTimeout();
+			}
+
+			@Test
+			@SneakyThrows
+			void shouldCloseInputStream() {
+				try {
+					waitForFinishedFileUpload();
+				} catch (TechnicalException e) {
+					// expected
+				}
+
+				verify(inputStream).close();
+			}
+		}
+
+		private void waitForFinishedFileUpload() {
+			service.waitForFinishedFileUpload(fileSender, inputStream);
+		}
+	}
 }
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcAttachmentFileTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcAttachmentFileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c954a3e170cdf1bafb583cb5cc59933637f665a
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/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.vorgang.vorgang.redirect;
+
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile;
+import de.ozgcloud.eingang.forwarding.GrpcAttachmentFile.Builder;
+import de.ozgcloud.vorgang.vorgang.IncomingFileGroupTestFactory;
+import de.ozgcloud.vorgang.vorgang.IncomingFileTestFactory;
+
+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_STR)
+				.setVendorId(IncomingFileTestFactory.VENDOR_ID)
+				.setSize(IncomingFileTestFactory.SIZE);
+	}
+
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcAttachmentTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcAttachmentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e0e278a9d017593627fa279c7837dfaa0d221c7
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/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.vorgang.vorgang.redirect;
+
+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/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcFileContentTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcFileContentTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..c37286a4df81c814c805fd2a6c05ac84a9741e68
--- /dev/null
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcFileContentTestFactory.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.vorgang.vorgang.redirect;
+
+import org.apache.commons.lang3.RandomUtils;
+
+import com.google.protobuf.ByteString;
+
+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 = RandomUtils.insecure().randomBytes(10);;
+
+	public static GrpcFileContent create() {
+		return createBuilder().build();
+	}
+
+	public static Builder createBuilder() {
+		return GrpcFileContent.newBuilder()
+				.setContent(ByteString.copyFrom(CONTENT))
+				.setIsEndOfFile(IS_END_OF_FILE);
+	}
+
+	public static GrpcFileContent createEndOfFile() {
+		return GrpcFileContent.newBuilder().setIsEndOfFile(true).build();
+	}
+
+}
diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcRouteForwardingRequestTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcRouteForwardingRequestTestFactory.java
index d09b7af528eab7e5bab4e7e7a1fbf5a44fa64103..58b939a4170938fb2b5a9f8d00879d98bbfef584 100644
--- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcRouteForwardingRequestTestFactory.java
+++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/redirect/GrpcRouteForwardingRequestTestFactory.java
@@ -23,10 +23,14 @@
  */
 package de.ozgcloud.vorgang.vorgang.redirect;
 
+import de.ozgcloud.eingang.forwarding.GrpcAttachment;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 
 class GrpcRouteForwardingRequestTestFactory {
 
+	public static final GrpcAttachment ATTACHMENT_METADATA = GrpcAttachmentTestFactory.createWithFile();
+	public static final GrpcAttachment ATTACHMENT_COTNENT = GrpcAttachmentTestFactory.createWithContent();
+
 	public static GrpcRouteForwardingRequest create() {
 		return createBuilder().build();
 	}
@@ -34,4 +38,16 @@ class GrpcRouteForwardingRequestTestFactory {
 	public static GrpcRouteForwardingRequest.Builder createBuilder() {
 		return GrpcRouteForwardingRequest.newBuilder();
 	}
+
+	public static GrpcRouteForwardingRequest createWithAttachmentMetadata() {
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setAttachment(ATTACHMENT_METADATA)
+				.build();
+	}
+
+	public static GrpcRouteForwardingRequest createWithAttachmentContent() {
+		return GrpcRouteForwardingRequest.newBuilder()
+				.setAttachment(ATTACHMENT_METADATA)
+				.build();
+	}
 }