diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cbda740f18643b4c39e6d1ca8170786cc83a34d9..551da4c33c3f98904758da15844180ca0514400c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,13 @@ variables:
   MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode"
   MAVEN_DEPLOY_CLI_OPTS: "--no-transfer-progress"
   MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
+  DOCKER_HOST: tcp://docker:2375
+  DOCKER_TLS_CERTDIR: ""
+  DOCKER_DRIVER: overlay2
+
+services:
+  - name: kopsh-docker-dockerhub.repo-ex.zcdi.dataport.de/docker:dind
+    alias: docker
 
 cache:
   paths:
diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClient.java b/src/main/java/de/ozgcloud/xta/client/XtaClient.java
index 53be82d1013dd48e8b6413dca6c7edae40fe281f..039964ab6fe25ccd74c30ae07dadb1468548791b 100644
--- a/src/main/java/de/ozgcloud/xta/client/XtaClient.java
+++ b/src/main/java/de/ozgcloud/xta/client/XtaClient.java
@@ -2,13 +2,12 @@ package de.ozgcloud.xta.client;
 
 import jakarta.validation.constraints.NotBlank;
 
-import org.apache.commons.lang3.NotImplementedException;
-
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.core.WrappedXtaService;
-import de.ozgcloud.xta.client.model.Identifier;
-import de.ozgcloud.xta.client.model.XtaMessage;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
+import de.ozgcloud.xta.client.model.XtaMessageAndTransportReport;
 import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing;
+import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException;
 import genv3.de.xoev.transport.xta.x211.PermissionDeniedException;
 import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException;
 import lombok.AccessLevel;
@@ -25,47 +24,60 @@ public class XtaClient {
 	private final XtaClientConfig config;
 
 	/**
-	 * Fetch metadata of pending messages sent to the {@code xtaIdentifier}.
-	 * The returned listing contains at most {@link de.ozgcloud.xta.client.config.XtaClientConfig#getMaxListItems() maxListItems} messages.
-	 * Use the {@code requestId} to {@link #close(String) close} pending messages.
-	 * To fetch the next messages, use {@link #getNextMessagesMetadata(String)}.
-	 * Note that {@code xtaIdentifier} has to be configured as a {@link de.ozgcloud.xta.client.config.XtaClientConfig#getClientIdentifiers() clientIdentifiers}.
+	 * Fetch metadata of pending messages sent to the {@code xtaIdentifier}. The returned listing contains at most
+	 * {@link de.ozgcloud.xta.client.config.XtaClientConfig#getMaxListItems() maxListItems} messages. To fetch the next messages, use
+	 * {@link #getNextMessagesMetadata(String)}. Note that {@code xtaIdentifier} has to be configured as a
+	 * {@link de.ozgcloud.xta.client.config.XtaClientConfig#getClientIdentifiers() clientIdentifiers}.
 	 *
-	 * @param xtaIdentifier the client identifier value to fetch messages for
+	 * @param clientIdentifier the client identifier value to fetch messages for
 	 * @return the listing result with metadata of messages
 	 */
-	public XtaMessageMetaDataListing getMessagesMetadata(@NotBlank String xtaIdentifier)
+	public XtaMessageMetaDataListing getMessagesMetadata(@NotBlank String clientIdentifier)
 			throws XTAWSTechnicalProblemException, PermissionDeniedException {
-		var clientIdentifier = deriveIdentifier(xtaIdentifier);
-		service.checkAccountActive(clientIdentifier);
-		return getStatusList(clientIdentifier);
+		var identifier = deriveIdentifier(clientIdentifier);
+		service.checkAccountActive(identifier);
+		return getStatusList(identifier);
 	}
 
 	/**
-	 * Fetch metadata of pending messages sent to the {@code xtaIdentifier}.
-	 * This method skips checks but otherwise behaves exactly as {@link #getMessagesMetadata(String)}.
+	 * Fetch metadata of pending messages sent to the {@code xtaIdentifier}. This method skips checks but otherwise behaves exactly as
+	 * {@link #getMessagesMetadata(String)}.
 	 */
-	public XtaMessageMetaDataListing getNextMessagesMetadata(@NotBlank String xtaIdentifier)
+	public XtaMessageMetaDataListing getNextMessagesMetadata(@NotBlank String clientIdentifier)
 			throws XTAWSTechnicalProblemException, PermissionDeniedException {
-		return getStatusList(deriveIdentifier(xtaIdentifier));
+		return getStatusList(deriveIdentifier(clientIdentifier));
 	}
 
-	private XtaMessageMetaDataListing getStatusList(Identifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
+	private XtaMessageMetaDataListing getStatusList(XtaIdentifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
 		return service.getStatusList(clientIdentifier, config.getMaxListItems());
 	}
 
-	Identifier deriveIdentifier(String xtaIdentifier) {
+	/**
+	 * Fetch the message content, close the message, and then fetch the transport report for the given {@code messageId} and reader identifier
+	 * {@code clientIdentifier}.
+	 *
+	 * @param clientIdentifier Identifier of the reading client
+	 * @param messageId        Identifier of the message to fetch
+	 * @return The message and transport report
+	 */
+	public XtaMessageAndTransportReport getMessage(@NotBlank String clientIdentifier, @NotBlank String messageId)
+			throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException {
+		var identifier = deriveIdentifier(clientIdentifier);
+
+		var message = service.getMessage(messageId, identifier);
+		service.close(messageId, identifier);
+
+		var transportReport = service.getTransportReport(messageId, identifier);
+		return XtaMessageAndTransportReport.builder()
+				.message(message)
+				.transportReport(transportReport)
+				.build();
+	}
+
+	XtaIdentifier deriveIdentifier(String xtaIdentifier) {
 		return config.getClientIdentifiers().stream()
 				.filter(id -> id.value().equals(xtaIdentifier))
 				.findFirst()
 				.orElseThrow(() -> new IllegalArgumentException("Unknown identifier: " + xtaIdentifier));
 	}
-
-	public XtaMessage getMessage(String messageId) {
-		throw new NotImplementedException("");
-	}
-
-	public void close(@NotBlank String messageId) {
-		throw new NotImplementedException("");
-	}
 }
diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java
index 1693d82a00092e5dc37e4a62fec5e1ff60cb91c0..184ce5b94144de8d8104d8da8fc46ffaf151cd69 100644
--- a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java
+++ b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java
@@ -19,7 +19,7 @@ import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Positive;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -32,7 +32,7 @@ import lombok.ToString;
 public class XtaClientConfig {
 
 	@NotEmpty(message = "at least one client identifier is required")
-	private final List<@Valid Identifier> clientIdentifiers;
+	private final List<@Valid XtaIdentifier> clientIdentifiers;
 
 	@NotBlank
 	private final String managementServiceUrl;
diff --git a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaService.java b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaService.java
index bb778caf5c32e0e8e0b7b5121a66173e247ccdc9..e64a923da2f2413ff337f1fc290cc223ec350230 100644
--- a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaService.java
+++ b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaService.java
@@ -2,9 +2,12 @@ package de.ozgcloud.xta.client.core;
 
 import jakarta.xml.ws.Holder;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.mapper.RequestMapper;
+import de.ozgcloud.xta.client.mapper.ResponseMapper;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing;
+import de.ozgcloud.xta.client.model.XtaTransportReport;
 import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException;
 import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException;
 import genv3.de.xoev.transport.xta.x211.MessageVirusDetectionException;
@@ -13,6 +16,7 @@ import genv3.de.xoev.transport.xta.x211.PermissionDeniedException;
 import genv3.de.xoev.transport.xta.x211.SyncAsyncException;
 import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType;
+import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -26,11 +30,26 @@ public class WrappedXtaService {
 	private final RequestMapper requestMapper;
 	private final ResponseMapper responseMapper;
 
-	public void checkAccountActive(Identifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
+	public void checkAccountActive(XtaIdentifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
 		ports.managementPort().checkAccountActive(requestMapper.mapPartyTypeFromIdentifier(clientIdentifier));
 	}
 
-	public XtaMessageMetaDataListing getStatusList(Identifier clientIdentifier, int maxListItems)
+	public XtaMessage getMessage(String messageId, XtaIdentifier clientIdentifier)
+			throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException {
+		final Holder<MessageMetaData> messageMetaDataHolder = new Holder<>();
+		var genericContentContainer = ports.msgBoxPort().getMessage(
+				requestMapper.mapMsgBoxFetchRequest(messageId),
+				requestMapper.mapPartyTypeFromIdentifier(clientIdentifier),
+				messageMetaDataHolder,
+				null
+		);
+		return responseMapper.mapXtaMessage(
+				genericContentContainer,
+				messageMetaDataHolder.value
+		);
+	}
+
+	public XtaMessageMetaDataListing getStatusList(XtaIdentifier clientIdentifier, int maxListItems)
 			throws XTAWSTechnicalProblemException, PermissionDeniedException {
 		final Holder<MsgBoxResponseType> fetchResponseHeader = new Holder<>();
 		var msgStatusListType = ports.msgBoxPort().getStatusList(
@@ -45,18 +64,18 @@ public class WrappedXtaService {
 			throws SyncAsyncException, XTAWSTechnicalProblemException, MessageVirusDetectionException, MessageSchemaViolationException,
 			PermissionDeniedException, ParameterIsNotValidException {
 		ports.sendPort().sendMessage(
-				requestMapper.mapGenericContentContainerFromXtaFile(message),
+				requestMapper.mapGenericContentContainer(message),
 				requestMapper.mapMessageMetaDataFromXtaMessageMetaData(message.metaData()),
 				null
 		);
 	}
 
-	public String createMessageId(Identifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
+	public String createMessageId(XtaIdentifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException {
 		return ports.managementPort().createMessageId(
 				requestMapper.mapPartyTypeFromIdentifier(clientIdentifier)).getValue();
 	}
 
-	public Boolean lookupService(String service, Identifier reader, Identifier clientIdentifier)
+	public Boolean lookupService(String service, XtaIdentifier reader, XtaIdentifier clientIdentifier)
 			throws XTAWSTechnicalProblemException, PermissionDeniedException, ParameterIsNotValidException {
 		return responseMapper.isServiceAvailable(
 				ports.managementPort().lookupService(
@@ -65,12 +84,21 @@ public class WrappedXtaService {
 		);
 	}
 
-	public void close(String messageId, String requestId, Identifier clientIdentifier)
+	public void close(String messageId, XtaIdentifier clientIdentifier)
 			throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException {
 		ports.msgBoxPort().close(
-				requestMapper.mapMsgBoxCloseRequestType(messageId, requestId),
+				requestMapper.mapMsgBoxCloseRequestType(messageId),
 				requestMapper.mapPartyTypeFromIdentifier(clientIdentifier)
 		);
 	}
 
+	public XtaTransportReport getTransportReport(String messageId, XtaIdentifier clientIdentifier)
+			throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException {
+		return responseMapper.mapXtaTransportReport(
+				ports.managementPort().getTransportReport(
+						requestMapper.mapAttributedURIType(messageId),
+						requestMapper.mapPartyTypeFromIdentifier(clientIdentifier)
+				));
+	}
+
 }
diff --git a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java
index 1db267b457bc18d2d87c3d633c931d728d1198b5..6a909b5709cce39cb5e369519fb28da62c56ee4a 100644
--- a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java
+++ b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java
@@ -4,6 +4,8 @@ import org.mapstruct.factory.Mappers;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.exception.ClientInitializationException;
+import de.ozgcloud.xta.client.mapper.RequestMapper;
+import de.ozgcloud.xta.client.mapper.ResponseMapper;
 import lombok.Builder;
 import lombok.RequiredArgsConstructor;
 
diff --git a/src/main/java/de/ozgcloud/xta/client/core/RequestMapper.java b/src/main/java/de/ozgcloud/xta/client/mapper/RequestMapper.java
similarity index 72%
rename from src/main/java/de/ozgcloud/xta/client/core/RequestMapper.java
rename to src/main/java/de/ozgcloud/xta/client/mapper/RequestMapper.java
index a04ca2e0587f13c315d40cdc6f7807614a7d19e6..dbcefae05f4cc08fd37cce805c24915d34ad658d 100644
--- a/src/main/java/de/ozgcloud/xta/client/core/RequestMapper.java
+++ b/src/main/java/de/ozgcloud/xta/client/mapper/RequestMapper.java
@@ -1,14 +1,11 @@
-package de.ozgcloud.xta.client.core;
-
-import java.util.List;
+package de.ozgcloud.xta.client.mapper;
 
 import org.apache.cxf.ws.addressing.AttributedURIType;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
-import org.mapstruct.Named;
 import org.mapstruct.ReportingPolicy;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import de.ozgcloud.xta.client.model.XtaFile;
 import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaData;
@@ -17,6 +14,7 @@ import genv3.de.xoev.transport.xta.x211.GenericContentContainer;
 import genv3.de.xoev.transport.xta.x211.LookupServiceRequest;
 import genv3.de.xoev.transport.xta.x211.LookupServiceType;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxCloseRequestType;
+import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxFetchRequest;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxStatusListRequestType;
 import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
 import genv3.eu.osci.ws.x2014.x10.transport.PartyIdentifierType;
@@ -33,12 +31,13 @@ public interface RequestMapper {
 	String MESSAGE_TYPE_LIST_VERSION_ID = "1.0";
 	String BUSINESS_SCENARIO_LIST_VERSION_ID = "1";
 	String IDENTIFIER_TYPE = "xoev";
+	String CLOSE_REQUEST_ID = "1";
 
 	@Mapping(target = "securityToken", ignore = true)
-	PartyType mapPartyTypeFromIdentifier(Identifier identifier);
+	PartyType mapPartyTypeFromIdentifier(XtaIdentifier identifier);
 
 	@Mapping(target = "type", constant = IDENTIFIER_TYPE)
-	PartyIdentifierType mapPartyIdentifierTypeFromIdentifier(Identifier value);
+	PartyIdentifierType mapPartyIdentifierTypeFromIdentifier(XtaIdentifier value);
 
 	@Mapping(target = "maxListItems", source = "maxListItems")
 	@Mapping(target = "listForm", constant = "MessageMetaData")
@@ -73,36 +72,29 @@ public interface RequestMapper {
 	@Mapping(target = "encryptedData", expression = "java(null)")
 	@Mapping(target = "contentContainer.message", source = "messageFile")
 	@Mapping(target = "contentContainer.attachment", source = "attachmentFiles")
-	GenericContentContainer mapGenericContentContainerFromXtaFile(XtaMessage xtaMessage);
+	GenericContentContainer mapGenericContentContainer(XtaMessage xtaMessage);
 
 	@Mapping(target = "value", source = "content")
 	@Mapping(target = "filename", source = "name")
 	@Mapping(target = "lang", source = "language")
-	ContentType mapContentTypeFromXtaFile(XtaFile xtaFile);
-
-	@Mapping(target = "lastMsgReceived", source = "messageId")
-	@Mapping(target = "msgBoxRequestID", source = "requestId")
-	MsgBoxCloseRequestType mapMsgBoxCloseRequestType(String messageId, String requestId);
-
-	default List<AttributedURIType> mapLastMsgReceived(String messageId) {
-		var attributedURIType = new AttributedURIType();
-		attributedURIType.setValue(messageId);
-		return List.of(attributedURIType);
-	}
-
-	@Mapping(target = "lookupServiceRequestList", expression = "java(mapLookupServiceRequestList(service, reader))")
-	LookupServiceRequest mapLookupServiceRequest(String service, Identifier reader);
-
-	@Named("mapLookupServiceRequestList")
-	default List<LookupServiceRequest.LookupServiceRequestList> mapLookupServiceRequestList(String service, Identifier reader) {
-		var lookupServiceRequestList = new LookupServiceRequest.LookupServiceRequestList();
-		lookupServiceRequestList.setLookupService(
-				mapLookupServiceType(
-						service,
-						reader
-				));
-		return List.of(lookupServiceRequestList);
-	}
-
-	LookupServiceType mapLookupServiceType(String serviceType, Identifier reader);
+	ContentType mapContentType(XtaFile xtaFile);
+
+	@Mapping(target = "lastMsgReceived", expression = "java( List.of(mapAttributedURIType(messageId)) )")
+	@Mapping(target = "msgBoxRequestID", constant = CLOSE_REQUEST_ID)
+	MsgBoxCloseRequestType mapMsgBoxCloseRequestType(String messageId);
+
+	@Mapping(target = "otherAttributes", ignore = true)
+	AttributedURIType mapAttributedURIType(String value);
+
+	@Mapping(target = "lookupServiceRequestList", expression = "java( List.of( mapLookupServiceRequestList(service, reader )) )")
+	LookupServiceRequest mapLookupServiceRequest(String service, XtaIdentifier reader);
+
+	@Mapping(target = "lookupService", expression = "java( mapLookupServiceType(service, reader) )")
+	LookupServiceRequest.LookupServiceRequestList mapLookupServiceRequestList(String service, XtaIdentifier reader);
+
+	LookupServiceType mapLookupServiceType(String serviceType, XtaIdentifier reader);
+
+	@Mapping(target = "msgSelector.messageID", expression = "java( List.of( mapAttributedURIType(string) ) )")
+	@Mapping(target = "msgPart", ignore = true)
+	MsgBoxFetchRequest mapMsgBoxFetchRequest(String messageId);
 }
diff --git a/src/main/java/de/ozgcloud/xta/client/core/ResponseMapper.java b/src/main/java/de/ozgcloud/xta/client/mapper/ResponseMapper.java
similarity index 54%
rename from src/main/java/de/ozgcloud/xta/client/core/ResponseMapper.java
rename to src/main/java/de/ozgcloud/xta/client/mapper/ResponseMapper.java
index 353fea20591e04c8b1a9f50e6130475c8ff8fc2a..473440b8274efcb6ad023c1407108f540f149910 100644
--- a/src/main/java/de/ozgcloud/xta/client/core/ResponseMapper.java
+++ b/src/main/java/de/ozgcloud/xta/client/mapper/ResponseMapper.java
@@ -1,17 +1,28 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.mapper;
 
+import java.time.ZonedDateTime;
 import java.util.Optional;
 
+import javax.xml.datatype.XMLGregorianCalendar;
+
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.ReportingPolicy;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaFile;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
+import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaData;
 import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing;
+import de.ozgcloud.xta.client.model.XtaMessageStatus;
+import de.ozgcloud.xta.client.model.XtaTransportReport;
+import genv3.de.xoev.transport.xta.x211.ContentType;
+import genv3.de.xoev.transport.xta.x211.GenericContentContainer;
 import genv3.de.xoev.transport.xta.x211.IsServiceAvailableValueType;
 import genv3.de.xoev.transport.xta.x211.LookupServiceResponse;
 import genv3.de.xoev.transport.xta.x211.LookupServiceResultType;
+import genv3.de.xoev.transport.xta.x211.MessageStatusType;
+import genv3.de.xoev.transport.xta.x211.TransportReport;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgStatusListType;
 import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
@@ -22,9 +33,8 @@ import genv3.eu.osci.ws.x2014.x10.transport.PartyIdentifierType;
 )
 public interface ResponseMapper {
 
-	Identifier mapIdentifierFromPartyIdentifierType(PartyIdentifierType partyIdentifierType);
+	XtaIdentifier mapIdentifierFromPartyIdentifierType(PartyIdentifierType partyIdentifierType);
 
-	@Mapping(target = "requestId", source = "response.msgBoxRequestID")
 	@Mapping(target = "pendingMessageCount", source = "response.itemsPending", defaultValue = "0")
 	@Mapping(target = "messages", source = "msgStatusListType.messageMetaData")
 	XtaMessageMetaDataListing mapXtaMessageMetaListing(MsgStatusListType msgStatusListType, MsgBoxResponseType response);
@@ -49,4 +59,31 @@ public interface ResponseMapper {
 				.map(Optional::ofNullable)
 				.allMatch(serviceAvailable -> serviceAvailable.orElse(false));
 	}
+
+	@Mapping(target = "content", source = "value")
+	@Mapping(target = "language", source = "lang")
+	@Mapping(target = "name", source = "filename")
+	XtaFile mapXtaFile(ContentType contentType);
+
+	@Mapping(target = "messageFile", source = "genericContentContainer.contentContainer.message")
+	@Mapping(target = "attachmentFiles", source = "genericContentContainer.contentContainer.attachment")
+	XtaMessage mapXtaMessage(GenericContentContainer genericContentContainer, MessageMetaData metaData);
+
+	@Mapping(target = "status", source = "messageStatus")
+	@Mapping(target = "metaData", source = "messageMetaData")
+	XtaTransportReport mapXtaTransportReport(TransportReport transportReport);
+
+	default ZonedDateTime mapZonedDateTime(XMLGregorianCalendar xmlGregorianCalendar) {
+		return xmlGregorianCalendar.toGregorianCalendar().toZonedDateTime();
+	}
+
+	default XtaMessageStatus mapXtaMessageStatus(MessageStatusType statusType) {
+		return switch (statusType.getStatus().intValue()) {
+			case 0 -> XtaMessageStatus.OPEN;
+			case 1 -> XtaMessageStatus.GREEN;
+			case 2 -> XtaMessageStatus.YELLOW;
+			case 3 -> XtaMessageStatus.RED;
+			default -> null;
+		};
+	}
 }
diff --git a/src/main/java/de/ozgcloud/xta/client/model/Identifier.java b/src/main/java/de/ozgcloud/xta/client/model/XtaIdentifier.java
similarity index 89%
rename from src/main/java/de/ozgcloud/xta/client/model/Identifier.java
rename to src/main/java/de/ozgcloud/xta/client/model/XtaIdentifier.java
index 831ea82f5684efad5627d6412d37731db7d3e5f1..8ea86a7eeeaea8640df9f3a9a3fc623d6e98ffd9 100644
--- a/src/main/java/de/ozgcloud/xta/client/model/Identifier.java
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaIdentifier.java
@@ -6,7 +6,7 @@ import jakarta.validation.constraints.NotBlank;
 import lombok.Builder;
 
 @Builder
-public record Identifier(
+public record XtaIdentifier(
 		@Nullable String name,
 		@Nullable String category,
 		@NotBlank String value
diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageAndTransportReport.java b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageAndTransportReport.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fed361806962dbb1f57c2a747ce65839cd79d4d
--- /dev/null
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageAndTransportReport.java
@@ -0,0 +1,13 @@
+package de.ozgcloud.xta.client.model;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import lombok.Builder;
+
+@Builder
+public record XtaMessageAndTransportReport(
+		@NotNull @Valid XtaMessage message,
+		@NotNull @Valid XtaTransportReport transportReport
+) {
+}
diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaData.java b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaData.java
index 87fee1fb81f3729aa310ccfe06dd12820a899320..707f4160ca6f837e89c8c7761df11deed673b6f0 100644
--- a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaData.java
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaData.java
@@ -16,8 +16,8 @@ public record XtaMessageMetaData(
 		@NotBlank String messageTypeCode,
 		@NotBlank String messageTypePayloadSchema,
 		@NotBlank String messageId,
-		@NotNull @Valid Identifier authorIdentifier,
-		@NotNull @Valid Identifier readerIdentifier,
+		@NotNull @Valid XtaIdentifier authorIdentifier,
+		@NotNull @Valid XtaIdentifier readerIdentifier,
 		@PositiveOrZero BigInteger messageSize
 ) {
 }
diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataListing.java b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataListing.java
index 11ae0246bbdb25520e6232ab1d23560ef3f43fab..b8af0b8ddfe5b4525caf3c7e15399adbb6301490 100644
--- a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataListing.java
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataListing.java
@@ -4,7 +4,6 @@ import java.math.BigInteger;
 import java.util.List;
 
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.PositiveOrZero;
 
@@ -12,7 +11,6 @@ import lombok.Builder;
 
 @Builder
 public record XtaMessageMetaDataListing(
-		@NotBlank String requestId,
 		@PositiveOrZero BigInteger pendingMessageCount,
 		@NotNull @Valid List<XtaMessageMetaData> messages
 ) {
diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaMessageStatus.java b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f43119f49063ff54013f81917ced31118d92655
--- /dev/null
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaMessageStatus.java
@@ -0,0 +1,15 @@
+package de.ozgcloud.xta.client.model;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public enum XtaMessageStatus {
+	OPEN(0),
+	GREEN(1),
+	YELLOW(2),
+	RED(3);
+
+	private final Integer code;
+}
diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaTransportReport.java b/src/main/java/de/ozgcloud/xta/client/model/XtaTransportReport.java
new file mode 100644
index 0000000000000000000000000000000000000000..c768d2a3bf7e5fe1ef6c88ce46fc474b32829358
--- /dev/null
+++ b/src/main/java/de/ozgcloud/xta/client/model/XtaTransportReport.java
@@ -0,0 +1,16 @@
+package de.ozgcloud.xta.client.model;
+
+import java.time.ZonedDateTime;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import lombok.Builder;
+
+@Builder
+public record XtaTransportReport(
+		@NotNull @Valid XtaMessageMetaData metaData,
+		@NotNull ZonedDateTime reportTime,
+		@NotNull XtaMessageStatus status
+) {
+}
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index 8ff45bec98154a00873c01b0219c0fb603a7c0ce..d14bccf3c7fbc8216c9518995eb53051c2b55c46 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -17,13 +17,13 @@
     </Appenders>
 
     <Loggers>
-        <!-- By default all logs from de.xoev.xta loggers are sent to Console -->
-        <Logger name="de.xoev.xta" level="debug" additivity="false">
+        <!-- By default all logs from de.ozgcloud.xta loggers are sent to Console -->
+        <Logger name="de.ozgcloud.xta" level="debug" additivity="false">
             <AppenderRef ref="Console" level="${env:LOG_LEVEL_STDOUT:-debug}" />
         </Logger>
 
         <!-- By default only error and fail levels logs from all other loggers are sent to Console appender. -->
-        <Root level="debug">
+        <Root level="error">
             <AppenderRef ref="Console" />
         </Root>
     </Loggers>
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java
index 2169e0ba07c193b013325927d89a87d45f2548aa..d8ff1c015e2db1633ce1ccf3a457a08761c74938 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java
@@ -9,6 +9,8 @@ import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import de.ozgcloud.xta.client.model.XtaMessageStatus;
+import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException;
 import lombok.SneakyThrows;
 
 class XtaClientITCase {
@@ -68,9 +70,51 @@ class XtaClientITCase {
 
 				assertThat(result.pendingMessageCount()).isZero();
 			}
+
 		}
 
 	}
 
+	@DisplayName("get message")
+	@Nested
+	class TestGetMessage {
+
+		private String messageId;
+
+		@BeforeEach
+		@SneakyThrows
+		void setup() {
+			messageId = XTA_TEST_SERVER_SETUP_EXTENSION.sendTestMessage();
+		}
+
+		@DisplayName("should return message with green status")
+		@Test
+		@SneakyThrows
+		void shouldReturnMessageWithGreenStatus() {
+			var result = client.getMessage(CLIENT_IDENTIFIER1.value(), messageId);
+
+			assertThat(result.message().metaData().messageId()).isEqualTo(messageId);
+			assertThat(result.transportReport().metaData().messageId()).isEqualTo(messageId);
+			assertThat(result.transportReport().status()).isEqualTo(XtaMessageStatus.GREEN);
+		}
+
+		@DisplayName("should throw invalid message id exception for modified message id")
+		@Test
+		void shouldThrowInvalidMessageIdExceptionForModifiedMessageId() {
+			assertThatThrownBy(() -> client.getMessage(CLIENT_IDENTIFIER1.value(), messageId + "1"))
+					.isInstanceOf(InvalidMessageIDException.class);
+		}
+
+
+		@DisplayName("should throw invalid message id exception for other client")
+		@Test
+		void shouldThrowInvalidMessageIdExceptionForOtherClient() {
+			assertThatThrownBy(() -> client.getMessage(CLIENT_IDENTIFIER2.value(), messageId))
+					.isInstanceOf(InvalidMessageIDException.class);
+		}
+
+
+	}
+
 }
 
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientManualITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientManualITCase.java
index 75ce79228220962acbe1a50207c205fb94cd0d39..51eb0674d4aa99ddffb1d2d8237fbeaaf5621dbd 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaClientManualITCase.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaClientManualITCase.java
@@ -10,6 +10,8 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import de.ozgcloud.xta.client.model.XtaMessageStatus;
+import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException;
 import lombok.SneakyThrows;
 
 @EnabledIfEnvironmentVariable(
@@ -79,5 +81,44 @@ class XtaClientManualITCase {
 
 	}
 
+	@DisplayName("get message")
+	@Nested
+	class TestGetMessage {
+
+		private String messageId;
+
+		@BeforeEach
+		@SneakyThrows
+		void setup() {
+			messageId = XTA_DEV_SERVER_SETUP_EXTENSION.sendTestMessage();
+		}
+
+		@DisplayName("should return message with green status")
+		@Test
+		@SneakyThrows
+		void shouldReturnMessageWithGreenStatus() {
+			var result = client.getMessage(CLIENT_IDENTIFIER1.value(), messageId);
+
+			assertThat(result.message().metaData().messageId()).isEqualTo(messageId);
+			assertThat(result.transportReport().metaData().messageId()).isEqualTo(messageId);
+			assertThat(result.transportReport().status()).isEqualTo(XtaMessageStatus.GREEN);
+		}
+
+		@DisplayName("should throw invalid message id exception for modified message id")
+		@Test
+		void shouldThrowInvalidMessageIdExceptionForModifiedMessageId() {
+			assertThatThrownBy(() -> client.getMessage(CLIENT_IDENTIFIER1.value(), messageId + "1"))
+					.isInstanceOf(InvalidMessageIDException.class);
+		}
+
+		@DisplayName("should throw invalid message id exception for other client")
+		@Test
+		void shouldThrowInvalidMessageIdExceptionForOtherClient() {
+			assertThatThrownBy(() -> client.getMessage(CLIENT_IDENTIFIER2.value(), messageId))
+					.isInstanceOf(InvalidMessageIDException.class);
+		}
+
+	}
+
 }
 
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java
index 6bb727373d8fc1c41fe9ac6c291a1c3853d2de06..4d1bd7a748c621ce4c5d6024d2559cfa49fd862d 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java
@@ -1,6 +1,7 @@
 package de.ozgcloud.xta.client;
 
-import static de.ozgcloud.xta.client.XtaClientConfigTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -18,7 +19,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.core.WrappedXtaService;
+import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing;
+import de.ozgcloud.xta.client.model.XtaTransportReport;
 import lombok.SneakyThrows;
 
 @ExtendWith(MockitoExtension.class)
@@ -119,4 +122,50 @@ class XtaClientTest {
 		}
 	}
 
+	@DisplayName("get message")
+	@Nested
+	class TestGetMessage {
+
+		@Mock
+		XtaMessage xtaMessage;
+
+		@Mock
+		XtaTransportReport xtaTransportReport;
+
+		@BeforeEach
+		@SneakyThrows
+		void mock() {
+			doReturn(SELF_IDENTIFIER).when(client).deriveIdentifier(SELF_IDENTIFIER_VALUE);
+			when(service.getMessage(MESSAGE_ID, SELF_IDENTIFIER)).thenReturn(xtaMessage);
+			when(service.getTransportReport(MESSAGE_ID, SELF_IDENTIFIER)).thenReturn(xtaTransportReport);
+		}
+
+		@DisplayName("should call close")
+		@Test
+		@SneakyThrows
+		void shouldCallClose() {
+			client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID);
+
+			verify(service).close(MESSAGE_ID, SELF_IDENTIFIER);
+		}
+
+		@DisplayName("should return with message")
+		@Test
+		@SneakyThrows
+		void shouldReturn() {
+			var result = client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID);
+
+			assertThat(result.message()).isEqualTo(xtaMessage);
+		}
+
+		@DisplayName("should return with transport report")
+		@Test
+		@SneakyThrows
+		void shouldReturnWithTransportReport() {
+			var result = client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID);
+
+			assertThat(result.transportReport()).isEqualTo(xtaTransportReport);
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaDevServerSetupExtension.java b/src/test/java/de/ozgcloud/xta/client/XtaDevServerSetupExtension.java
index 1dd74acb9d9c3d938548e860d20a8599232e9314..e8f64e5a49edd2246ff120a581f718c3b6a4fa73 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaDevServerSetupExtension.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaDevServerSetupExtension.java
@@ -12,7 +12,7 @@ import com.google.common.io.Files;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.core.WrappedXtaService;
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import de.ozgcloud.xta.client.model.XtaMessageMetaData;
 import genv3.de.xoev.transport.xta.x211.CodeFehlernummer;
 import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException;
@@ -35,17 +35,17 @@ public class XtaDevServerSetupExtension implements BeforeAllCallback, BeforeEach
 	private XtaClientFactory clientFactory;
 
 	static final String BASE_URL = "https://li33-0005.dp.dsecurecloud.de/MB_XTA-WS/XTA210";
-	static final Identifier CLIENT_IDENTIFIER3 = Identifier.builder()
+	static final XtaIdentifier CLIENT_IDENTIFIER3 = XtaIdentifier.builder()
 			.value("gad:010103000000")
 			.category("DMS Schleswig-Holstein")
 			.name("Generischer Antragsdienst")
 			.build();
-	static final Identifier CLIENT_IDENTIFIER2 = Identifier.builder()
+	static final XtaIdentifier CLIENT_IDENTIFIER2 = XtaIdentifier.builder()
 			.value("gae:test-environment@ozg-cloud.de")
 			.category("Generischer Antragsempfänger")
 			.name("OZG-Cloud Test")
 			.build();
-	static final Identifier CLIENT_IDENTIFIER1 = Identifier.builder()
+	static final XtaIdentifier CLIENT_IDENTIFIER1 = XtaIdentifier.builder()
 			.value("gae:dev-environment@ozg-cloud.de")
 			.category("Generischer Antragsempfänger")
 			.name("OZG-Cloud Dev")
@@ -92,12 +92,12 @@ public class XtaDevServerSetupExtension implements BeforeAllCallback, BeforeEach
 	}
 
 	@SneakyThrows
-	public void sendTestMessage() {
-		sendTestMessage(CLIENT_IDENTIFIER1, CLIENT_IDENTIFIER1);
+	public String sendTestMessage() {
+		return sendTestMessage(CLIENT_IDENTIFIER1, CLIENT_IDENTIFIER1);
 	}
 
 	@SneakyThrows
-	void sendTestMessage(Identifier author, Identifier reader) {
+	String sendTestMessage(XtaIdentifier author, XtaIdentifier reader) {
 		var messageId = service.createMessageId(author);
 		var message = XtaMessageExampleLoader.load(
 				XtaMessageExampleLoader.MessageExampleConfig.builder()
@@ -125,6 +125,7 @@ public class XtaDevServerSetupExtension implements BeforeAllCallback, BeforeEach
 			logCodeFehlerNummer(e.getFaultInfo().getErrorCode());
 			throw e;
 		}
+		return messageId;
 	}
 
 	private void logCodeFehlerNummer(CodeFehlernummer fehlernummer) {
@@ -132,13 +133,13 @@ public class XtaDevServerSetupExtension implements BeforeAllCallback, BeforeEach
 	}
 
 	@SneakyThrows
-	private void closeAllMessages(Identifier clientId) {
+	private void closeAllMessages(XtaIdentifier clientId) {
 		var result = service.getStatusList(clientId, 100);
 		var messageIds = result.messages().stream()
 				.map(XtaMessageMetaData::messageId)
 				.toList();
 		for (var messageId : messageIds) {
-			service.close(messageId, result.requestId(), clientId);
+			service.close(messageId, clientId);
 		}
 	}
 
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaMessageExampleLoader.java b/src/test/java/de/ozgcloud/xta/client/XtaMessageExampleLoader.java
index ad668eff8abde1588d92f9031be1bc0f4300272b..4dfbf0247458fdb7291d300b9552a22d8ef23a79 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaMessageExampleLoader.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaMessageExampleLoader.java
@@ -1,6 +1,6 @@
 package de.ozgcloud.xta.client;
 
-import static de.ozgcloud.xta.client.ZipFileTestFactory.*;
+import static de.ozgcloud.xta.client.factory.ZipFileTestFactory.*;
 import static java.util.stream.Collectors.*;
 
 import java.io.IOException;
@@ -25,7 +25,7 @@ import org.apache.commons.io.IOUtils;
 import org.jetbrains.annotations.NotNull;
 import org.yaml.snakeyaml.Yaml;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import de.ozgcloud.xta.client.model.XtaFile;
 import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaData;
@@ -63,7 +63,7 @@ public class XtaMessageExampleLoader {
 		return textContent -> fillTemplateString(textContent, templateValues);
 	}
 
-	private static String identifierPart(Identifier identifier, int index) {
+	private static String identifierPart(XtaIdentifier identifier, int index) {
 		return identifier == null ? "?" : identifier.value().split(":", 2)[index];
 	}
 
@@ -71,8 +71,8 @@ public class XtaMessageExampleLoader {
 	public record MessageExampleConfig(
 			@NotBlank String messageLabel,
 			@Nullable String messageId,
-			@Nullable Identifier author,
-			@Nullable Identifier reader) {
+			@Nullable XtaIdentifier author,
+			@Nullable XtaIdentifier reader) {
 	}
 
 	@SuppressWarnings("unchecked")
@@ -129,9 +129,9 @@ public class XtaMessageExampleLoader {
 		return configValue != null ? configValue : supplier.get();
 	}
 
-	private static Identifier mapIdentifier(Map<String, Object> identifier) {
+	private static XtaIdentifier mapIdentifier(Map<String, Object> identifier) {
 		Function<String, String> getString = key -> (String) identifier.get(key);
-		return Identifier.builder()
+		return XtaIdentifier.builder()
 				.name(getString.apply("name"))
 				.value(getString.apply("value"))
 				.category(getString.apply("category"))
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java b/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java
index d7f3dd2178b85745881e1fbaa050d8153f6e8fa1..2e89fb1a70af925110d7f4748ad0264528aa6a4c 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java
+++ b/src/test/java/de/ozgcloud/xta/client/XtaTestServerSetupExtension.java
@@ -13,7 +13,7 @@ import org.testcontainers.utility.DockerImageName;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.core.WrappedXtaService;
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import de.ozgcloud.xta.client.model.XtaMessageMetaData;
 import genv3.de.xoev.transport.xta.x211.CodeFehlernummer;
 import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException;
@@ -108,12 +108,12 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC
 	}
 
 	@SneakyThrows
-	public void sendTestMessage() {
-		sendTestMessage(CLIENT_IDENTIFIER1, CLIENT_IDENTIFIER1);
+	public String sendTestMessage() {
+		return sendTestMessage(CLIENT_IDENTIFIER1, CLIENT_IDENTIFIER1);
 	}
 
 	@SneakyThrows
-	void sendTestMessage(Identifier author, Identifier reader) {
+	String sendTestMessage(XtaIdentifier author, XtaIdentifier reader) {
 		var messageId = service.createMessageId(author);
 		var message = XtaMessageExampleLoader.load(
 				XtaMessageExampleLoader.MessageExampleConfig.builder()
@@ -141,6 +141,7 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC
 			logCodeFehlerNummer(e.getFaultInfo().getErrorCode());
 			throw e;
 		}
+		return messageId;
 	}
 
 	private void logCodeFehlerNummer(CodeFehlernummer fehlernummer) {
@@ -148,13 +149,13 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC
 	}
 
 	@SneakyThrows
-	private void closeAllMessages(Identifier clientId) {
+	private void closeAllMessages(XtaIdentifier clientId) {
 		var result = service.getStatusList(clientId, 100);
 		var messageIds = result.messages().stream()
 				.map(XtaMessageMetaData::messageId)
 				.toList();
 		for (var messageId : messageIds) {
-			service.close(messageId, result.requestId(), clientId);
+			service.close(messageId, clientId);
 		}
 	}
 
diff --git a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java
index c1d2c496e7f88c98be3ad7c2d2bb07aa75e764b6..dec358ad2fae5816809e0a51e9aed38835c4d6dc 100644
--- a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import de.ozgcloud.xta.client.XtaClientConfigTestFactory;
+import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory;
 import de.ozgcloud.xta.client.exception.ClientInitializationException;
 import lombok.SneakyThrows;
 
diff --git a/src/test/java/de/ozgcloud/xta/client/core/ResponseMapperTest.java b/src/test/java/de/ozgcloud/xta/client/core/ResponseMapperTest.java
deleted file mode 100644
index 97011943f6fdd1bf10c95d6d5f00902eaf8ca2c4..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/xta/client/core/ResponseMapperTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package de.ozgcloud.xta.client.core;
-
-import static de.ozgcloud.xta.client.core.MessageMetaDataTestFactory.*;
-import static de.ozgcloud.xta.client.core.MsgBoxResponseTypeTestFactory.*;
-import static org.assertj.core.api.Assertions.*;
-
-import java.math.BigInteger;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.mapstruct.factory.Mappers;
-
-import de.ozgcloud.xta.client.model.XtaMessageMetaData;
-import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType;
-import genv3.eu.osci.ws.x2008.x05.transport.MsgStatusListType;
-
-class ResponseMapperTest {
-	private ResponseMapper mapper;
-
-	@BeforeEach
-	void setup() {
-		mapper = Mappers.getMapper(ResponseMapper.class);
-	}
-
-	@DisplayName("map xta message meta listing")
-	@Nested
-	class TestMapXtaMessageMetaListing {
-
-		private MsgStatusListType msgStatusListType;
-		private MsgBoxResponseType msgBoxResponseTypeWithPendingMessages;
-		private MsgBoxResponseType msgBoxResponseTypeWithNoPendingMessages;
-
-		@BeforeEach
-		void setup() {
-			msgStatusListType = MsgStatusListTypeTestFactory.create();
-			msgBoxResponseTypeWithPendingMessages = MsgBoxResponseTypeTestFactory
-					.createWithPendingMessages();
-			msgBoxResponseTypeWithNoPendingMessages = MsgBoxResponseTypeTestFactory
-					.createWithNoPendingMessages();
-		}
-
-		@DisplayName("should map request Id")
-		@Test
-		void shouldMapRequestId() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
-
-			assertThat(result.requestId()).isEqualTo(MSG_BOX_REQUEST_ID);
-		}
-
-		@DisplayName("should map pending message count")
-		@Test
-		void shouldMapPendingMessageCount() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
-
-			assertThat(result.pendingMessageCount()).isEqualTo(ITEMS_PENDING);
-		}
-
-		@DisplayName("should map pending message count to zero if missing")
-		@Test
-		void shouldMapMoreMessagesToFalseWithNoPendingMessages() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithNoPendingMessages);
-
-			assertThat(result.pendingMessageCount()).isEqualTo(BigInteger.ZERO);
-		}
-
-		@DisplayName("should map message id")
-		@Test
-		void shouldMapMessageId() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
-
-			var messageIds = result.messages().stream()
-					.map(XtaMessageMetaData::messageId)
-					.toList();
-			assertThat(messageIds).containsExactly(MESSAGE_ID, MESSAGE_ID2, MESSAGE_ID3);
-		}
-
-		@DisplayName("should map message type code")
-		@Test
-		void shouldMapMessageTypeCode() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
-
-			var messageTypes = result.messages().stream()
-					.map(XtaMessageMetaData::messageTypeCode)
-					.toList();
-			assertThat(messageTypes).containsExactly(
-					MESSAGE_TYPE_CODE, MESSAGE_TYPE_CODE2, MESSAGE_TYPE_CODE3);
-		}
-
-		@DisplayName("should map service")
-		@Test
-		void shouldMapService() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.service()).isEqualTo(MESSAGE_SERVICE);
-		}
-
-		@DisplayName("should map business scenario code")
-		@Test
-		void shouldMapBusinessScenarioCode() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.businessScenarioCode()).isEqualTo(BUSINESS_SCENARIO_CODE);
-		}
-
-		@DisplayName("should map message type payload schema")
-		@Test
-		void shouldMapMessageTypePayloadSchema() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.messageTypePayloadSchema()).isEqualTo(MESSAGE_TYPE_PAYLOAD_SCHEMA);
-		}
-
-		@DisplayName("should map author identifier")
-		@Test
-		void shouldMapAuthorIdentifier() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.authorIdentifier()).isEqualTo(AUTHOR_IDENTIFIER);
-		}
-
-		@DisplayName("should map reader identifier")
-		@Test
-		void shouldMapReaderIdentifier() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.readerIdentifier()).isEqualTo(READER_IDENTIFIER);
-		}
-
-		@DisplayName("should map size")
-		@Test
-		void shouldMapSize() {
-			var result = getFirstMappedMetaData();
-
-			assertThat(result.messageSize()).isEqualTo(MESSAGE_SIZE);
-		}
-
-		private XtaMessageMetaData getFirstMappedMetaData() {
-			var result = mapper.mapXtaMessageMetaListing(
-					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
-
-			return result.messages().getFirst();
-		}
-	}
-
-	@DisplayName("is service available")
-	@Nested
-	class TestIsServiceAvailable {
-		@DisplayName("should return")
-		@ParameterizedTest
-		@ValueSource(booleans = { true, false })
-		void shouldReturnTrue(boolean serviceAvailable) {
-			var result = mapper.isServiceAvailable(LookupServiceResponseTestFactory.create(serviceAvailable));
-
-			assertThat(result).isEqualTo(serviceAvailable);
-		}
-
-		@DisplayName("should return false with null")
-		@Test
-		void shouldReturnFalseWithNull() {
-			var result = mapper.isServiceAvailable(LookupServiceResponseTestFactory.create(null));
-
-			assertThat(result).isEqualTo(false);
-		}
-	}
-}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactoryTest.java
index b1f9c8d427f479e5dd2d975b74cb72bc1abc550b..72e464197c42ba287c9ce6f4925def198065633d 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactoryTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactoryTest.java
@@ -13,6 +13,8 @@ import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
+import de.ozgcloud.xta.client.mapper.RequestMapper;
+import de.ozgcloud.xta.client.mapper.ResponseMapper;
 import lombok.SneakyThrows;
 
 @ExtendWith(MockitoExtension.class)
diff --git a/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceTest.java b/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceTest.java
index 7b06178b0354e0b5442b595bc148f5da8453af21..07764a6803c188dfb911c2659559d53bb3b76a87 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/core/WrappedXtaServiceTest.java
@@ -1,8 +1,7 @@
 package de.ozgcloud.xta.client.core;
 
-import static de.ozgcloud.xta.client.XtaClientConfigTestFactory.*;
-import static de.ozgcloud.xta.client.core.MessageMetaDataTestFactory.*;
-import static de.ozgcloud.xta.client.core.MsgBoxResponseTypeTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
@@ -20,16 +19,21 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import de.ozgcloud.xta.client.factory.XtaMessageTestFactory;
+import de.ozgcloud.xta.client.mapper.RequestMapper;
+import de.ozgcloud.xta.client.mapper.ResponseMapper;
 import de.ozgcloud.xta.client.model.XtaMessage;
 import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing;
-import de.ozgcloud.xta.client.model.XtaMessageTestFactory;
+import de.ozgcloud.xta.client.model.XtaTransportReport;
 import genv3.de.xoev.transport.xta.x211.GenericContentContainer;
 import genv3.de.xoev.transport.xta.x211.LookupServiceRequest;
 import genv3.de.xoev.transport.xta.x211.LookupServiceResponse;
 import genv3.de.xoev.transport.xta.x211.ManagementPortType;
 import genv3.de.xoev.transport.xta.x211.MsgBoxPortType;
 import genv3.de.xoev.transport.xta.x211.SendPortType;
+import genv3.de.xoev.transport.xta.x211.TransportReport;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxCloseRequestType;
+import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxFetchRequest;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxStatusListRequestType;
 import genv3.eu.osci.ws.x2008.x05.transport.MsgStatusListType;
@@ -52,6 +56,55 @@ class WrappedXtaServiceTest {
 	@InjectMocks
 	private WrappedXtaService service;
 
+	@DisplayName("get message")
+	@Nested
+	class TestGetMessage {
+
+		@Mock
+		private MsgBoxPortType msgBoxPortType;
+
+		@Mock
+		private MsgBoxFetchRequest msgBoxFetchRequest;
+
+		@Mock
+		private PartyType partyType;
+
+		@Mock
+		private MessageMetaData messageMetaData;
+
+		@Mock
+		private GenericContentContainer genericContentContainer;
+
+		@Mock
+		private XtaMessage xtaMessage;
+
+		@BeforeEach
+		@SneakyThrows
+		void mock() {
+			when(xtaPortTriple.msgBoxPort()).thenReturn(msgBoxPortType);
+			when(requestMapper.mapPartyTypeFromIdentifier(SELF_IDENTIFIER)).thenReturn(partyType);
+			when(requestMapper.mapMsgBoxFetchRequest(MESSAGE_ID)).thenReturn(msgBoxFetchRequest);
+
+			when(msgBoxPortType.getMessage(eq(msgBoxFetchRequest), eq(partyType), any(), eq(null))).thenAnswer(invocation -> {
+				@SuppressWarnings("unchecked")
+				var metaData = (Holder<MessageMetaData>) invocation.getArgument(2);
+				metaData.value = messageMetaData;
+				return genericContentContainer;
+			});
+			when(responseMapper.mapXtaMessage(genericContentContainer, messageMetaData))
+					.thenReturn(xtaMessage);
+		}
+
+		@DisplayName("should return message")
+		@Test
+		@SneakyThrows
+		void shouldReturnMessage() {
+			var result = service.getMessage(MESSAGE_ID, SELF_IDENTIFIER);
+
+			assertThat(result).isEqualTo(xtaMessage);
+		}
+	}
+
 	@DisplayName("check account active")
 	@Nested
 	class TestCheckAccountActive {
@@ -146,7 +199,7 @@ class WrappedXtaServiceTest {
 		void mock() {
 			xtaMessage = XtaMessageTestFactory.create();
 			when(xtaPortTriple.sendPort()).thenReturn(sendPortType);
-			when(requestMapper.mapGenericContentContainerFromXtaFile(xtaMessage)).thenReturn(genericContentContainer);
+			when(requestMapper.mapGenericContentContainer(xtaMessage)).thenReturn(genericContentContainer);
 			when(requestMapper.mapMessageMetaDataFromXtaMessageMetaData(xtaMessage.metaData())).thenReturn(messageMetaData);
 		}
 
@@ -208,7 +261,7 @@ class WrappedXtaServiceTest {
 		@BeforeEach
 		void mock() {
 			when(xtaPortTriple.msgBoxPort()).thenReturn(msgBoxPortType);
-			when(requestMapper.mapMsgBoxCloseRequestType(MESSAGE_ID, MSG_BOX_REQUEST_ID)).thenReturn(closeRequestType);
+			when(requestMapper.mapMsgBoxCloseRequestType(MESSAGE_ID)).thenReturn(closeRequestType);
 			when(requestMapper.mapPartyTypeFromIdentifier(SELF_IDENTIFIER)).thenReturn(partyType);
 		}
 
@@ -216,7 +269,7 @@ class WrappedXtaServiceTest {
 		@Test
 		@SneakyThrows
 		void shouldReturn() {
-			service.close(MESSAGE_ID, MSG_BOX_REQUEST_ID, SELF_IDENTIFIER);
+			service.close(MESSAGE_ID, SELF_IDENTIFIER);
 
 			verify(msgBoxPortType).close(closeRequestType, partyType);
 		}
@@ -262,4 +315,43 @@ class WrappedXtaServiceTest {
 		}
 	}
 
+	@DisplayName("get transport report")
+	@Nested
+	class TestGetTransportReport {
+
+		@Mock
+		private ManagementPortType managementPortType;
+
+		@Mock
+		private AttributedURIType messageIdType;
+
+		@Mock
+		private PartyType authorPartyType;
+
+		@Mock
+		private TransportReport transportReport;
+
+		@Mock
+		private XtaTransportReport xtaTransportReport;
+
+		@BeforeEach
+		@SneakyThrows
+		void mock() {
+			when(xtaPortTriple.managementPort()).thenReturn(managementPortType);
+			when(requestMapper.mapAttributedURIType(MESSAGE_ID)).thenReturn(messageIdType);
+			when(requestMapper.mapPartyTypeFromIdentifier(SELF_IDENTIFIER)).thenReturn(authorPartyType);
+			when(managementPortType.getTransportReport(messageIdType, authorPartyType)).thenReturn(transportReport);
+			when(responseMapper.mapXtaTransportReport(transportReport)).thenReturn(xtaTransportReport);
+		}
+
+		@DisplayName("should return")
+		@Test
+		@SneakyThrows
+		void shouldReturn() {
+			var result = service.getTransportReport(MESSAGE_ID, SELF_IDENTIFIER);
+
+			assertThat(result).isEqualTo(xtaTransportReport);
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaPortTripleFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaPortTripleFactoryTest.java
index bf9e24fb00fedcfd0478229b6daf9cb11c797c37..90e018553a79734a97b8c563dae446c15a5de61e 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/XtaPortTripleFactoryTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/core/XtaPortTripleFactoryTest.java
@@ -30,7 +30,7 @@ import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import de.ozgcloud.xta.client.XtaClientConfigTestFactory;
+import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory;
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import genv3.de.xoev.transport.xta.x211.ManagementPortType;
 import genv3.de.xoev.transport.xta.x211.MsgBoxPortType;
diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java
index 294fcb065701b97d5db3e703233dfa675af0d9af..3993d1f7e5e5bbeff430651361900932970b1e3d 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java
@@ -1,6 +1,6 @@
 package de.ozgcloud.xta.client.core;
 
-import static de.ozgcloud.xta.client.XtaClientConfigTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*;
 import static de.ozgcloud.xta.client.core.XtaTLSClientParametersFactory.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
@@ -22,7 +22,7 @@ import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import de.ozgcloud.xta.client.XtaClientConfigTestFactory;
+import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory;
 import de.ozgcloud.xta.client.config.XtaClientConfig;
 import de.ozgcloud.xta.client.exception.ClientInitializationException;
 import lombok.SneakyThrows;
diff --git a/src/test/java/de/ozgcloud/xta/client/factory/ContentTypeTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/ContentTypeTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..98060f7881e6e8b56dc232804d5073cc286b413e
--- /dev/null
+++ b/src/test/java/de/ozgcloud/xta/client/factory/ContentTypeTestFactory.java
@@ -0,0 +1,32 @@
+package de.ozgcloud.xta.client.factory;
+
+import static de.ozgcloud.xta.client.factory.XtaFileTestFactory.*;
+
+import jakarta.activation.DataHandler;
+import jakarta.mail.util.ByteArrayDataSource;
+
+import genv3.de.xoev.transport.xta.x211.ContentType;
+
+public class ContentTypeTestFactory {
+
+	public static ContentType create() {
+		return createWithId(XTA_FILE_ID);
+	}
+
+	public static ContentType createWithId(String messageId) {
+		var content = new ContentType();
+		content.setValue(createDataHandler(XTA_FILE_CONTENT));
+		content.setContentDescription(XTA_FILE_CONTENT_DESCRIPTION);
+		content.setContentType(XTA_FILE_CONTENT_TYPE);
+		content.setEncoding(XTA_FILE_ENCODING);
+		content.setFilename(XTA_FILE_NAME);
+		content.setId(messageId);
+		content.setLang(XTA_FILE_LANGUAGE);
+		content.setSize(XTA_FILE_SIZE);
+		return content;
+	}
+
+	private static DataHandler createDataHandler(byte[] contentTypeContent) {
+		return new DataHandler(new ByteArrayDataSource(contentTypeContent, "application/octet-stream"));
+	}
+}
diff --git a/src/test/java/de/ozgcloud/xta/client/factory/GenericContentContainerTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/GenericContentContainerTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f4dba1fe92d1d7940564e322f68727878994a8a
--- /dev/null
+++ b/src/test/java/de/ozgcloud/xta/client/factory/GenericContentContainerTestFactory.java
@@ -0,0 +1,25 @@
+package de.ozgcloud.xta.client.factory;
+
+import static de.ozgcloud.xta.client.factory.XtaFileTestFactory.*;
+
+import java.util.List;
+
+import genv3.de.xoev.transport.xta.x211.GenericContentContainer;
+
+public class GenericContentContainerTestFactory {
+	public static GenericContentContainer create() {
+		var genericContentContainer = new GenericContentContainer();
+		genericContentContainer.setContentContainer(createContainer());
+		return genericContentContainer;
+	}
+
+	private static GenericContentContainer.ContentContainer createContainer() {
+		var contentContainer = new GenericContentContainer.ContentContainer();
+		contentContainer.setMessage(ContentTypeTestFactory.create());
+		contentContainer.getAttachment().addAll(List.of(
+				ContentTypeTestFactory.createWithId(XTA_FILE_ID_2),
+				ContentTypeTestFactory.createWithId(XTA_FILE_ID_3)
+		));
+		return contentContainer;
+	}
+}
diff --git a/src/test/java/de/ozgcloud/xta/client/core/LookupServiceResponseTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/LookupServiceResponseTestFactory.java
similarity index 97%
rename from src/test/java/de/ozgcloud/xta/client/core/LookupServiceResponseTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/LookupServiceResponseTestFactory.java
index 3bf1be57190889a2a67a43a629cda724c599c15f..4798fc3dedb0d276675dfc62dcff98c752b4d989 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/LookupServiceResponseTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/LookupServiceResponseTestFactory.java
@@ -1,4 +1,4 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.factory;
 
 import genv3.de.xoev.transport.xta.x211.IsServiceAvailableValueType;
 import genv3.de.xoev.transport.xta.x211.LookupServiceResponse;
diff --git a/src/test/java/de/ozgcloud/xta/client/core/MessageMetaDataTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/MessageMetaDataTestFactory.java
similarity index 86%
rename from src/test/java/de/ozgcloud/xta/client/core/MessageMetaDataTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/MessageMetaDataTestFactory.java
index 4ef9fbbc96bad15856ab2b7c38f9447284a96f0e..e8f44dd04692818d1d8dd5203e7c6c741ad8f0fa 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/MessageMetaDataTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/MessageMetaDataTestFactory.java
@@ -1,10 +1,10 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.factory;
 
-import static de.ozgcloud.xta.client.core.RequestMapper.*;
+import static de.ozgcloud.xta.client.mapper.RequestMapper.*;
 
 import java.math.BigInteger;
 
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
 import genv3.eu.osci.ws.x2014.x10.transport.PartyIdentifierType;
 
@@ -15,12 +15,12 @@ public class MessageMetaDataTestFactory {
 	public static final String MESSAGE_TYPE_PAYLOAD_SCHEMA = "message-type-payload-schema";
 	public static final String BUSINESS_SCENARIO_CODE = "businessScenarioCode";
 
-	public static final Identifier AUTHOR_IDENTIFIER = Identifier.builder()
+	public static final XtaIdentifier AUTHOR_IDENTIFIER = XtaIdentifier.builder()
 			.value("authorIdentifier")
 			.name("authorIdentifierName")
 			.category("authorIdentifierCategory")
 			.build();
-	public static final Identifier READER_IDENTIFIER = Identifier.builder()
+	public static final XtaIdentifier READER_IDENTIFIER = XtaIdentifier.builder()
 			.value("readerIdentifier")
 			.name("readerIdentifierName")
 			.category("readerIdentifierCategory")
@@ -75,7 +75,7 @@ public class MessageMetaDataTestFactory {
 		return messageMetaData;
 	}
 
-	private static void assignIdentifier(Identifier identifier, PartyIdentifierType target) {
+	private static void assignIdentifier(XtaIdentifier identifier, PartyIdentifierType target) {
 		target.setType(IDENTIFIER_TYPE);
 		target.setValue(identifier.value());
 		target.setName(identifier.name());
diff --git a/src/test/java/de/ozgcloud/xta/client/core/MsgBoxResponseTypeTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/MsgBoxResponseTypeTestFactory.java
similarity index 96%
rename from src/test/java/de/ozgcloud/xta/client/core/MsgBoxResponseTypeTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/MsgBoxResponseTypeTestFactory.java
index 85eab3df7a0a5fa248642c051bc10f5053a20dc9..1846eda4c576e8eb283f4242ae8e277ef5f5c0c9 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/MsgBoxResponseTypeTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/MsgBoxResponseTypeTestFactory.java
@@ -1,4 +1,4 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.factory;
 
 import java.math.BigInteger;
 
diff --git a/src/test/java/de/ozgcloud/xta/client/core/MsgStatusListTypeTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/MsgStatusListTypeTestFactory.java
similarity index 85%
rename from src/test/java/de/ozgcloud/xta/client/core/MsgStatusListTypeTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/MsgStatusListTypeTestFactory.java
index e9c9e7a37f4c9fdab8f76f63b33bd7b442f341ee..ed5a23c1257d16bf5a027017f84d0f5ffcf9e187 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/MsgStatusListTypeTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/MsgStatusListTypeTestFactory.java
@@ -1,6 +1,6 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.factory;
 
-import static de.ozgcloud.xta.client.core.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
 
 import java.util.List;
 
diff --git a/src/test/java/de/ozgcloud/xta/client/factory/TransportReportTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/TransportReportTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c898dbf46b0f7735affe5a7e70218bb94824cd3
--- /dev/null
+++ b/src/test/java/de/ozgcloud/xta/client/factory/TransportReportTestFactory.java
@@ -0,0 +1,49 @@
+package de.ozgcloud.xta.client.factory;
+
+import java.math.BigInteger;
+import java.time.ZonedDateTime;
+import java.util.GregorianCalendar;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+
+import de.ozgcloud.xta.client.model.XtaMessageStatus;
+import genv3.de.xoev.transport.xta.x211.MessageStatusType;
+import genv3.de.xoev.transport.xta.x211.TransportReport;
+
+public class TransportReportTestFactory {
+
+	public static final ZonedDateTime REPORT_TIME = ZonedDateTime.now();
+	private static final DatatypeFactory datatypeFactory;
+
+	static {
+		try {
+			datatypeFactory = DatatypeFactory.newInstance();
+		} catch (DatatypeConfigurationException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static TransportReport create() {
+		return createWithStatus(XtaMessageStatus.OPEN);
+	}
+
+	public static TransportReport createWithStatus(XtaMessageStatus status) {
+		var transportReport = new TransportReport();
+
+		transportReport.setReportTime(
+				datatypeFactory.newXMLGregorianCalendar(GregorianCalendar.from(REPORT_TIME))
+		);
+		transportReport.setMessageMetaData(MessageMetaDataTestFactory.create());
+		transportReport.setMessageStatus(createMessageStatus(status));
+
+		return transportReport;
+	}
+
+	private static MessageStatusType createMessageStatus(XtaMessageStatus status) {
+		var messageStatus = new MessageStatusType();
+		messageStatus.setStatus(BigInteger.valueOf(status.getCode()));
+		return messageStatus;
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaClientConfigTestFactory.java
similarity index 93%
rename from src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/XtaClientConfigTestFactory.java
index 1fb4ec73b4082b1d430fba8ef101a960b3a88456..0a4a4ea9af55462404c09f2a111bb4805d1a7b6f 100644
--- a/src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaClientConfigTestFactory.java
@@ -1,4 +1,4 @@
-package de.ozgcloud.xta.client;
+package de.ozgcloud.xta.client.factory;
 
 import java.io.ByteArrayOutputStream;
 import java.math.BigInteger;
@@ -16,19 +16,19 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 
 import de.ozgcloud.xta.client.config.XtaClientConfig;
-import de.ozgcloud.xta.client.model.Identifier;
+import de.ozgcloud.xta.client.model.XtaIdentifier;
 import lombok.SneakyThrows;
 
 public class XtaClientConfigTestFactory {
 
 	public static final String SELF_IDENTIFIER_VALUE = "dp:selfIdentifier";
-	public static final Identifier SELF_IDENTIFIER = Identifier.builder()
+	public static final XtaIdentifier SELF_IDENTIFIER = XtaIdentifier.builder()
 			.value(SELF_IDENTIFIER_VALUE)
 			.category("Generic category")
 			.name("Generic Name")
 			.build();
 	public static final int MAX_LIST_ITEMS = 10;
-	static final Identifier SELF_IDENTIFIER2 = Identifier.builder()
+	public static final XtaIdentifier SELF_IDENTIFIER2 = XtaIdentifier.builder()
 			.value("dp:selfIdentifier2")
 			.category("Generic category")
 			.name("Generic Name")
diff --git a/src/test/java/de/ozgcloud/xta/client/model/XtaFileTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaFileTestFactory.java
similarity index 86%
rename from src/test/java/de/ozgcloud/xta/client/model/XtaFileTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/XtaFileTestFactory.java
index d458ab9a231462719bcd9202b0967652b8cb6a7f..313140857b296a696bf7f9c98c54ddd48a6ba105 100644
--- a/src/test/java/de/ozgcloud/xta/client/model/XtaFileTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaFileTestFactory.java
@@ -1,10 +1,12 @@
-package de.ozgcloud.xta.client.model;
+package de.ozgcloud.xta.client.factory;
 
 import java.math.BigInteger;
 
 import jakarta.activation.DataHandler;
 import jakarta.mail.util.ByteArrayDataSource;
 
+import de.ozgcloud.xta.client.model.XtaFile;
+
 public class XtaFileTestFactory {
 
 	public static byte[] XTA_FILE_CONTENT = "Testinhalt".getBytes();
@@ -13,6 +15,8 @@ public class XtaFileTestFactory {
 	public static String XTA_FILE_ENCODING = "UTF-8";
 	public static String XTA_FILE_NAME = "test.txt";
 	public static String XTA_FILE_ID = "test-file-id";
+	public static String XTA_FILE_ID_2 = "test-file-id2";
+	public static String XTA_FILE_ID_3 = "test-file-id2";
 	public static String XTA_FILE_LANGUAGE = "de";
 
 	public static BigInteger XTA_FILE_SIZE = BigInteger.valueOf(XTA_FILE_CONTENT.length);
diff --git a/src/test/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataTestFactory.java
similarity index 76%
rename from src/test/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataTestFactory.java
index 6b58908ef3daa3769341691bad0db1f961a9b0fa..9a139902781424e880e3452fe73d58bac26d2ec1 100644
--- a/src/test/java/de/ozgcloud/xta/client/model/XtaMessageMetaDataTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataTestFactory.java
@@ -1,6 +1,8 @@
-package de.ozgcloud.xta.client.model;
+package de.ozgcloud.xta.client.factory;
 
-import static de.ozgcloud.xta.client.core.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
+
+import de.ozgcloud.xta.client.model.XtaMessageMetaData;
 
 public class XtaMessageMetaDataTestFactory {
 
diff --git a/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..88607275987ed72ef4dcb9492e5c6093dbb4a70e
--- /dev/null
+++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageTestFactory.java
@@ -0,0 +1,24 @@
+package de.ozgcloud.xta.client.factory;
+
+import static de.ozgcloud.xta.client.factory.XtaFileTestFactory.*;
+
+import java.util.List;
+
+import de.ozgcloud.xta.client.model.XtaMessage;
+
+public class XtaMessageTestFactory {
+
+	public static XtaMessage create() {
+		return createBuilder().build();
+	}
+
+	public static XtaMessage.XtaMessageBuilder createBuilder() {
+		return XtaMessage.builder()
+				.metaData(XtaMessageMetaDataTestFactory.create())
+				.messageFile(XtaFileTestFactory.create())
+				.attachmentFiles(List.of(
+						XtaFileTestFactory.createBuilder().id(XTA_FILE_ID_2).build(),
+						XtaFileTestFactory.createBuilder().id(XTA_FILE_ID_3).build()
+				));
+	}
+}
diff --git a/src/test/java/de/ozgcloud/xta/client/ZipFileTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/ZipFileTestFactory.java
similarity index 98%
rename from src/test/java/de/ozgcloud/xta/client/ZipFileTestFactory.java
rename to src/test/java/de/ozgcloud/xta/client/factory/ZipFileTestFactory.java
index 61d99801869cecbd1a0e3caf2a3c2acd1d6a6272..bd9ee19d77ed6932db50f1000ec76b6730787825 100644
--- a/src/test/java/de/ozgcloud/xta/client/ZipFileTestFactory.java
+++ b/src/test/java/de/ozgcloud/xta/client/factory/ZipFileTestFactory.java
@@ -1,4 +1,4 @@
-package de.ozgcloud.xta.client;
+package de.ozgcloud.xta.client.factory;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
diff --git a/src/test/java/de/ozgcloud/xta/client/core/RequestMapperTest.java b/src/test/java/de/ozgcloud/xta/client/mapper/RequestMapperTest.java
similarity index 64%
rename from src/test/java/de/ozgcloud/xta/client/core/RequestMapperTest.java
rename to src/test/java/de/ozgcloud/xta/client/mapper/RequestMapperTest.java
index c224700c91878b2eac05237d65ce8713260ecd70..307d607b5623c0be3631f6d4e3c509c2216ff740 100644
--- a/src/test/java/de/ozgcloud/xta/client/core/RequestMapperTest.java
+++ b/src/test/java/de/ozgcloud/xta/client/mapper/RequestMapperTest.java
@@ -1,22 +1,23 @@
-package de.ozgcloud.xta.client.core;
+package de.ozgcloud.xta.client.mapper;
 
-import static de.ozgcloud.xta.client.XtaClientConfigTestFactory.*;
-import static de.ozgcloud.xta.client.core.MessageMetaDataTestFactory.*;
-import static de.ozgcloud.xta.client.core.MsgBoxResponseTypeTestFactory.*;
-import static de.ozgcloud.xta.client.core.RequestMapper.*;
-import static de.ozgcloud.xta.client.model.XtaFileTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaFileTestFactory.*;
+import static de.ozgcloud.xta.client.mapper.RequestMapper.*;
 import static org.assertj.core.api.Assertions.*;
 
-import org.apache.cxf.helpers.IOUtils;
+import org.apache.commons.io.IOUtils;
 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 de.ozgcloud.xta.client.factory.XtaFileTestFactory;
+import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory;
+import de.ozgcloud.xta.client.factory.XtaMessageTestFactory;
+import de.ozgcloud.xta.client.model.XtaFile;
 import de.ozgcloud.xta.client.model.XtaMessage;
-import de.ozgcloud.xta.client.model.XtaMessageMetaDataTestFactory;
-import de.ozgcloud.xta.client.model.XtaMessageTestFactory;
 import genv3.de.xoev.transport.xta.x211.ContentType;
 import genv3.de.xoev.transport.xta.x211.LookupServiceType;
 import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
@@ -25,12 +26,7 @@ import lombok.SneakyThrows;
 
 class RequestMapperTest {
 
-	private RequestMapper mapper;
-
-	@BeforeEach
-	void setup() {
-		mapper = Mappers.getMapper(RequestMapper.class);
-	}
+	private final RequestMapper mapper = Mappers.getMapper(RequestMapper.class);
 
 	@DisplayName("map party type from identifier")
 	@Nested
@@ -191,9 +187,9 @@ class RequestMapperTest {
 		}
 	}
 
-	@DisplayName("map generic content container from xta file")
+	@DisplayName("map generic content container")
 	@Nested
-	class TestMapGenericContentContainerFromXtaFile {
+	class TestMapGenericContentContainer {
 		private XtaMessage xtaMessage;
 
 		@BeforeEach
@@ -201,36 +197,110 @@ class RequestMapperTest {
 			xtaMessage = XtaMessageTestFactory.create();
 		}
 
-		@DisplayName("should map message value")
+		@DisplayName("should map message")
 		@Test
 		@SneakyThrows
-		void shouldMapMessageValue() {
-			var result = mapper.mapGenericContentContainerFromXtaFile(xtaMessage);
+		void shouldMapMessage() {
+			var result = mapper.mapGenericContentContainer(xtaMessage);
 
-			assertThat(getContent(result.getContentContainer().getMessage())).isEqualTo(XTA_FILE_CONTENT);
+			var messageFileId = result.getContentContainer().getMessage().getId();
+			assertThat(messageFileId).isEqualTo(XTA_FILE_ID);
 		}
 
-		@DisplayName("should map attachment value")
+		@DisplayName("should map attachments")
 		@Test
 		@SneakyThrows
-		void shouldMapAttachmentValue() {
-			var result = mapper.mapGenericContentContainerFromXtaFile(xtaMessage);
+		void shouldMapAttachments() {
+			var result = mapper.mapGenericContentContainer(xtaMessage);
 
-			assertThat(getContent(result.getContentContainer().getAttachment().getFirst())).isEqualTo(XTA_FILE_CONTENT);
+			var attachmentFileIds = result.getContentContainer().getAttachment().stream()
+					.map(ContentType::getId)
+					.toList();
+			assertThat(attachmentFileIds).containsExactly(XTA_FILE_ID_2, XTA_FILE_ID_3);
 		}
 
 		@DisplayName("should set encrypted data to null")
 		@Test
 		void shouldSetEncryptedDataToNull() {
-			var result = mapper.mapGenericContentContainerFromXtaFile(xtaMessage);
+			var result = mapper.mapGenericContentContainer(xtaMessage);
 
 			assertThat(result.getEncryptedData()).isNull();
 		}
 
+	}
+
+	@DisplayName("map content type")
+	@Nested
+	class TestMapContentType {
+		private final XtaFile xtaFile = XtaFileTestFactory.create();
+
+		@DisplayName("should map value")
+		@Test
 		@SneakyThrows
-		private byte[] getContent(ContentType contentType) {
-			var dataHandler = contentType.getValue();
-			return IOUtils.readBytesFromStream(dataHandler.getInputStream());
+		void shouldMapValue() {
+			var result = doMapping();
+
+			assertThat(IOUtils.toByteArray(result.getValue().getInputStream())).isEqualTo(XTA_FILE_CONTENT);
+		}
+
+		@DisplayName("should map content description")
+		@Test
+		void shouldMapContentDescription() {
+			var result = doMapping();
+
+			assertThat(result.getContentDescription()).isEqualTo(XTA_FILE_CONTENT_DESCRIPTION);
+		}
+
+		@DisplayName("should map content type")
+		@Test
+		void shouldMapContentType() {
+			var result = doMapping();
+
+			assertThat(result.getContentType()).isEqualTo(XTA_FILE_CONTENT_TYPE);
+		}
+
+		@DisplayName("should map encoding")
+		@Test
+		void shouldMapEncoding() {
+			var result = doMapping();
+
+			assertThat(result.getEncoding()).isEqualTo(XTA_FILE_ENCODING);
+		}
+
+		@DisplayName("should map filename")
+		@Test
+		void shouldMapFilename() {
+			var result = doMapping();
+
+			assertThat(result.getFilename()).isEqualTo(XTA_FILE_NAME);
+		}
+
+		@DisplayName("should map id")
+		@Test
+		void shouldMapId() {
+			var result = doMapping();
+
+			assertThat(result.getId()).isEqualTo(XTA_FILE_ID);
+		}
+
+		@DisplayName("should map lang")
+		@Test
+		void shouldMapLang() {
+			var result = doMapping();
+
+			assertThat(result.getLang()).isEqualTo(XTA_FILE_LANGUAGE);
+		}
+
+		@DisplayName("should map size")
+		@Test
+		void shouldMapSize() {
+			var result = doMapping();
+
+			assertThat(result.getSize()).isEqualTo(XTA_FILE_SIZE);
+		}
+
+		private ContentType doMapping() {
+			return mapper.mapContentType(xtaFile);
 		}
 	}
 
@@ -240,7 +310,7 @@ class RequestMapperTest {
 		@DisplayName("should map message id")
 		@Test
 		void shouldMapMessageId() {
-			var result = mapper.mapMsgBoxCloseRequestType(MESSAGE_ID, MSG_BOX_REQUEST_ID);
+			var result = mapper.mapMsgBoxCloseRequestType(MESSAGE_ID);
 
 			assertThat(result.getLastMsgReceived().getFirst().getValue()).isEqualTo(MESSAGE_ID);
 		}
@@ -248,9 +318,9 @@ class RequestMapperTest {
 		@DisplayName("should map request id")
 		@Test
 		void shouldMapRequestId() {
-			var result = mapper.mapMsgBoxCloseRequestType(MESSAGE_ID, MSG_BOX_REQUEST_ID);
+			var result = mapper.mapMsgBoxCloseRequestType(MESSAGE_ID);
 
-			assertThat(result.getMsgBoxRequestID()).isEqualTo(MSG_BOX_REQUEST_ID);
+			assertThat(result.getMsgBoxRequestID()).isEqualTo(CLOSE_REQUEST_ID);
 		}
 	}
 
@@ -282,4 +352,16 @@ class RequestMapperTest {
 
 	}
 
+	@DisplayName("map msg box fetch request")
+	@Nested
+	class TestMapMsgBoxFetchRequest {
+		@DisplayName("should map message id")
+		@Test
+		void shouldMapMessageId() {
+			var result = mapper.mapMsgBoxFetchRequest(MESSAGE_ID);
+
+			assertThat(result.getMsgSelector().getMessageID().getFirst().getValue()).isEqualTo(MESSAGE_ID);
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/xta/client/mapper/ResponseMapperTest.java b/src/test/java/de/ozgcloud/xta/client/mapper/ResponseMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..91c45392b41825b0676892d00858cb5d2f3066b8
--- /dev/null
+++ b/src/test/java/de/ozgcloud/xta/client/mapper/ResponseMapperTest.java
@@ -0,0 +1,353 @@
+package de.ozgcloud.xta.client.mapper;
+
+import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*;
+import static de.ozgcloud.xta.client.factory.MsgBoxResponseTypeTestFactory.*;
+import static de.ozgcloud.xta.client.factory.TransportReportTestFactory.*;
+import static de.ozgcloud.xta.client.factory.XtaFileTestFactory.*;
+import static java.time.temporal.ChronoUnit.*;
+import static org.assertj.core.api.Assertions.*;
+
+import java.math.BigInteger;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mapstruct.factory.Mappers;
+
+import de.ozgcloud.xta.client.factory.ContentTypeTestFactory;
+import de.ozgcloud.xta.client.factory.GenericContentContainerTestFactory;
+import de.ozgcloud.xta.client.factory.LookupServiceResponseTestFactory;
+import de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory;
+import de.ozgcloud.xta.client.factory.MsgBoxResponseTypeTestFactory;
+import de.ozgcloud.xta.client.factory.MsgStatusListTypeTestFactory;
+import de.ozgcloud.xta.client.factory.TransportReportTestFactory;
+import de.ozgcloud.xta.client.model.XtaFile;
+import de.ozgcloud.xta.client.model.XtaMessage;
+import de.ozgcloud.xta.client.model.XtaMessageMetaData;
+import de.ozgcloud.xta.client.model.XtaMessageStatus;
+import de.ozgcloud.xta.client.model.XtaTransportReport;
+import genv3.de.xoev.transport.xta.x211.ContentType;
+import genv3.de.xoev.transport.xta.x211.GenericContentContainer;
+import genv3.de.xoev.transport.xta.x211.TransportReport;
+import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType;
+import genv3.eu.osci.ws.x2008.x05.transport.MsgStatusListType;
+import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData;
+import lombok.SneakyThrows;
+
+class ResponseMapperTest {
+	private ResponseMapper mapper;
+
+	@BeforeEach
+	void setup() {
+		mapper = Mappers.getMapper(ResponseMapper.class);
+	}
+
+	@DisplayName("map xta message meta listing")
+	@Nested
+	class TestMapXtaMessageMetaListing {
+		private MsgStatusListType msgStatusListType;
+		private MsgBoxResponseType msgBoxResponseTypeWithPendingMessages;
+		private MsgBoxResponseType msgBoxResponseTypeWithNoPendingMessages;
+
+		@BeforeEach
+		void setup() {
+			msgStatusListType = MsgStatusListTypeTestFactory.create();
+			msgBoxResponseTypeWithPendingMessages = MsgBoxResponseTypeTestFactory
+					.createWithPendingMessages();
+			msgBoxResponseTypeWithNoPendingMessages = MsgBoxResponseTypeTestFactory
+					.createWithNoPendingMessages();
+		}
+
+		@DisplayName("should map pending message count")
+		@Test
+		void shouldMapPendingMessageCount() {
+			var result = mapper.mapXtaMessageMetaListing(
+					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
+
+			assertThat(result.pendingMessageCount()).isEqualTo(ITEMS_PENDING);
+		}
+
+		@DisplayName("should map pending message count to zero if missing")
+		@Test
+		void shouldMapMoreMessagesToFalseWithNoPendingMessages() {
+			var result = mapper.mapXtaMessageMetaListing(
+					msgStatusListType, msgBoxResponseTypeWithNoPendingMessages);
+
+			assertThat(result.pendingMessageCount()).isEqualTo(BigInteger.ZERO);
+		}
+
+		@DisplayName("should map meta data")
+		@Test
+		void shouldMapMetaData() {
+			var result = mapper.mapXtaMessageMetaListing(
+					msgStatusListType, msgBoxResponseTypeWithPendingMessages);
+
+			var messageIds = result.messages().stream()
+					.map(XtaMessageMetaData::messageId)
+					.toList();
+			assertThat(messageIds)
+					.containsExactly(MESSAGE_ID, MESSAGE_ID2, MESSAGE_ID3);
+		}
+	}
+
+	@DisplayName("map xta message meta")
+	@Nested
+	class TestMapXtaMessageMeta {
+
+		private final MessageMetaData messageMetaData = MessageMetaDataTestFactory.create();
+
+		@DisplayName("should map message id")
+		@Test
+		void shouldMapMessageId() {
+			var result = doMapping();
+
+			assertThat(result.messageId()).isEqualTo(MESSAGE_ID);
+		}
+
+		@DisplayName("should map message type code")
+		@Test
+		void shouldMapMessageTypeCode() {
+			var result = doMapping();
+
+			assertThat(result.messageTypeCode()).isEqualTo(MESSAGE_TYPE_CODE);
+		}
+
+		@DisplayName("should map service")
+		@Test
+		void shouldMapService() {
+			var result = doMapping();
+
+			assertThat(result.service()).isEqualTo(MESSAGE_SERVICE);
+		}
+
+		@DisplayName("should map business scenario code")
+		@Test
+		void shouldMapBusinessScenarioCode() {
+			var result = doMapping();
+
+			assertThat(result.businessScenarioCode()).isEqualTo(BUSINESS_SCENARIO_CODE);
+		}
+
+		@DisplayName("should map message type payload schema")
+		@Test
+		void shouldMapMessageTypePayloadSchema() {
+			var result = doMapping();
+
+			assertThat(result.messageTypePayloadSchema()).isEqualTo(MESSAGE_TYPE_PAYLOAD_SCHEMA);
+		}
+
+		@DisplayName("should map author identifier")
+		@Test
+		void shouldMapAuthorIdentifier() {
+			var result = doMapping();
+
+			assertThat(result.authorIdentifier()).isEqualTo(AUTHOR_IDENTIFIER);
+		}
+
+		@DisplayName("should map reader identifier")
+		@Test
+		void shouldMapReaderIdentifier() {
+			var result = doMapping();
+
+			assertThat(result.readerIdentifier()).isEqualTo(READER_IDENTIFIER);
+		}
+
+		@DisplayName("should map size")
+		@Test
+		void shouldMapSize() {
+			var result = doMapping();
+
+			assertThat(result.messageSize()).isEqualTo(MESSAGE_SIZE);
+		}
+
+		private XtaMessageMetaData doMapping() {
+			return mapper.mapXtaMessageMetaData(messageMetaData);
+		}
+	}
+
+	@DisplayName("is service available")
+	@Nested
+	class TestIsServiceAvailable {
+		@DisplayName("should return")
+		@ParameterizedTest
+		@ValueSource(booleans = { true, false })
+		void shouldReturnTrue(boolean serviceAvailable) {
+			var result = mapper.isServiceAvailable(LookupServiceResponseTestFactory.create(serviceAvailable));
+
+			assertThat(result).isEqualTo(serviceAvailable);
+		}
+
+		@DisplayName("should return false with null")
+		@Test
+		void shouldReturnFalseWithNull() {
+			var result = mapper.isServiceAvailable(LookupServiceResponseTestFactory.create(null));
+
+			assertThat(result).isFalse();
+		}
+	}
+
+	@DisplayName("map xta file")
+	@Nested
+	class TestMapXtaFile {
+
+		private final ContentType contentType = ContentTypeTestFactory.create();
+
+		@DisplayName("should map content")
+		@Test
+		@SneakyThrows
+		void shouldMapContent() {
+			var result = doMapping();
+
+			assertThat(IOUtils.toByteArray(result.content().getInputStream())).isEqualTo(XTA_FILE_CONTENT);
+		}
+
+		@DisplayName("should map content description")
+		@Test
+		void shouldMapContentDescription() {
+			var result = doMapping();
+
+			assertThat(result.contentDescription()).isEqualTo(XTA_FILE_CONTENT_DESCRIPTION);
+		}
+
+		@DisplayName("should map content type")
+		@Test
+		void shouldMapContentType() {
+			var result = doMapping();
+
+			assertThat(result.contentType()).isEqualTo(XTA_FILE_CONTENT_TYPE);
+		}
+
+		@DisplayName("should map encoding")
+		@Test
+		void shouldMapEncoding() {
+			var result = doMapping();
+
+			assertThat(result.encoding()).isEqualTo(XTA_FILE_ENCODING);
+		}
+
+		@DisplayName("should map name")
+		@Test
+		void shouldMapName() {
+			var result = doMapping();
+
+			assertThat(result.name()).isEqualTo(XTA_FILE_NAME);
+		}
+
+		@DisplayName("should map id")
+		@Test
+		void shouldMapId() {
+			var result = doMapping();
+
+			assertThat(result.id()).isEqualTo(XTA_FILE_ID);
+		}
+
+		@DisplayName("should map language")
+		@Test
+		void shouldMapLanguage() {
+			var result = doMapping();
+
+			assertThat(result.language()).isEqualTo(XTA_FILE_LANGUAGE);
+		}
+
+		@DisplayName("should map size")
+		@Test
+		void shouldMapSize() {
+			var result = doMapping();
+
+			assertThat(result.size()).isEqualTo(XTA_FILE_SIZE);
+		}
+
+		private XtaFile doMapping() {
+			return mapper.mapXtaFile(contentType);
+		}
+	}
+
+	@DisplayName("map xta message")
+	@Nested
+	class TestMapXtaMessage {
+
+		private final GenericContentContainer genericContentContainer = GenericContentContainerTestFactory.create();
+		private final MessageMetaData metaData = MessageMetaDataTestFactory.create();
+
+		@DisplayName("should map meta data")
+		@Test
+		void shouldMapMetaData() {
+			var result = doMapping();
+
+			assertThat(result.metaData())
+					.usingRecursiveComparison()
+					.isEqualTo(mapper.mapXtaMessageMetaData(metaData));
+		}
+
+		@DisplayName("should map message file")
+		@Test
+		void shouldMapMessageFile() {
+			var result = doMapping();
+
+			assertThat(result.messageFile())
+					.usingRecursiveComparison()
+					.isEqualTo(mapper.mapXtaFile(genericContentContainer.getContentContainer().getMessage()));
+		}
+
+		@DisplayName("should map attachment files")
+		@Test
+		void shouldMapAttachmentFiles() {
+			var result = doMapping();
+
+			var attachmentFileIds = result.attachmentFiles().stream()
+					.map(XtaFile::id)
+					.toList();
+			assertThat(attachmentFileIds).containsExactly(XTA_FILE_ID_2, XTA_FILE_ID_3);
+		}
+
+		private XtaMessage doMapping() {
+			return mapper.mapXtaMessage(genericContentContainer, metaData);
+		}
+	}
+
+	@DisplayName("map xta transport report")
+	@Nested
+	class TestMapXtaTransportReport {
+
+		private final TransportReport transportReport = TransportReportTestFactory.create();
+
+		@DisplayName("should map meta data")
+		@Test
+		void shouldMapMetaData() {
+			var result = doMapping();
+
+			assertThat(result.metaData())
+					.usingRecursiveComparison()
+					.isEqualTo(mapper.mapXtaMessageMetaData(transportReport.getMessageMetaData()));
+		}
+
+		@DisplayName("should map report time")
+		@Test
+		void shouldMapReportTime() {
+			var result = doMapping();
+
+			assertThat(result.reportTime())
+					.isEqualTo(REPORT_TIME.truncatedTo(MILLIS));
+		}
+
+		private XtaTransportReport doMapping() {
+			return mapper.mapXtaTransportReport(transportReport);
+		}
+
+		@DisplayName("should map status")
+		@ParameterizedTest
+		@EnumSource(XtaMessageStatus.class)
+		void shouldMapStatus(XtaMessageStatus status) {
+			var result = mapper.mapXtaTransportReport(TransportReportTestFactory.createWithStatus(status));
+
+			assertThat(result.status())
+					.isEqualTo(status);
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/xta/client/model/XtaMessageTestFactory.java b/src/test/java/de/ozgcloud/xta/client/model/XtaMessageTestFactory.java
deleted file mode 100644
index a6c2e238967dc75fa1183695a7ef5ba9a6db68c3..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/xta/client/model/XtaMessageTestFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package de.ozgcloud.xta.client.model;
-
-import java.util.List;
-
-public class XtaMessageTestFactory {
-
-	public static String XTA_MESSAGE_FILE_NAME = "messageFile.txt";
-	public static String XTA_MESSAGE_FILE_ID = "message-file-id";
-
-	public static XtaMessage create() {
-		return createBuilder().build();
-	}
-
-	public static XtaMessage.XtaMessageBuilder createBuilder() {
-		return XtaMessage.builder()
-				.metaData(XtaMessageMetaDataTestFactory.create())
-				.messageFile(XtaFileTestFactory.createBuilder()
-						.name(XTA_MESSAGE_FILE_NAME)
-						.id(XTA_MESSAGE_FILE_ID)
-						.build())
-				.attachmentFiles(List.of(XtaFileTestFactory.create()));
-	}
-}
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
new file mode 100644
index 0000000000000000000000000000000000000000..71f815985778d395c5f74d051be77a7e04cbf83b
--- /dev/null
+++ b/src/test/resources/log4j2.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration name="log4j2Config" status="WARN">
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%date %-5level [%t] %c{1.} - %msg%n" />
+            <filters>
+                <ThresholdFilter level="${env:LOG_LEVEL_STDOUT:-debug}" onMatch="ACCEPT"
+                    onMismatch="DENY" />
+            </filters>
+        </Console>
+    </Appenders>
+
+    <Loggers>
+        <Logger name="de.ozgcloud.xta" level="debug" additivity="false">
+            <AppenderRef ref="Console" level="${env:LOG_LEVEL_STDOUT:-debug}" />
+        </Logger>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+    </Loggers>
+
+</Configuration>