diff --git a/README.md b/README.md
index 6e907988589a1f031fd27e97b1c8313b10c8fc4b..494476882a9fbd458a96d64bf8d9e7331657c118 100644
--- a/README.md
+++ b/README.md
@@ -512,7 +512,6 @@ var submissionBaseUrl = "https://submission-api-testing.fit-connect.fitko.dev";
 ```
 
 ## Roadmap
-
 - [ ] Add auto-reject on technical errors
 - [ ] Maven central release of 1.0.0-beta
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java b/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
index 5c57e4aa7fa47ba9f2d40b0697ab5c34537e91d9..1b4755b4378e0ecf7c3b71b41dc72b16331338ee 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
@@ -17,13 +17,21 @@ import java.util.stream.Collectors;
 @AllArgsConstructor
 public class ApplicationConfig {
 
+    @Builder.Default
+    public static final String AUTH_TAG_SPLIT_TOKEN = "\\.";
+
     @Builder.Default
     private String httpProxyHost = "";
+
     @Builder.Default
     private Integer httpProxyPort = 0;
+
     @Builder.Default
     private Integer requestTimeoutInSeconds = 30;
 
+    @Builder.Default
+    private boolean enableAutoReject = true;
+
     @Builder.Default
     private URI setSchemaWriteVersion = SchemaConfig.SET_V_1_0_1.getSchemaUri();
 
@@ -120,5 +128,9 @@ public class ApplicationConfig {
                 .map(EnvironmentName::getName)
                 .collect(Collectors.joining(" | "));
     }
+
+    public boolean isProxySet() {
+        return (httpProxyPort != null && httpProxyPort > 0) && (httpProxyHost != null || !httpProxyHost.isEmpty());
+    }
 }
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/EventClaimFields.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/EventClaimFields.java
index 8f23d61fe7a5aa3b8dfeaab39f01ef7179e4e690..5218c2cb26f2cea6ab1b1654d8ecdf9213ece729 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/EventClaimFields.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/EventClaimFields.java
@@ -9,5 +9,7 @@ public final class EventClaimFields {
     public static final String CLAIM_SCHEMA = "$schema";
     public static final String CLAIM_TXN = "txn";
     public static final String CLAIM_SUB = "sub";
+    public static final String AUTHENTICATION_TAGS = "authenticationTags";
+    public static  final String PROBLEMS = "problems";
     public static final String HEADER_TYPE = "secevent+jwt";
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/authtags/AuthenticationTags.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/authtags/AuthenticationTags.java
index fd09244fb4cdb267673a111279c8586ccfc22dee..9f7594ead7cf5139888f3dc5d8fcd26d7f9da16c 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/authtags/AuthenticationTags.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/authtags/AuthenticationTags.java
@@ -7,7 +7,9 @@ import java.util.UUID;
 
 @Data
 public class AuthenticationTags {
+
     private String metadata;
     private String data;
     private Map<UUID, String> attachments;
+
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/Problem.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/Problem.java
index 2b6313a6ed29f5ab8bc763ecf53f8d2d52d38299..6504666420bd3f791e1b5b86bdb13040e8081213 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/Problem.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/Problem.java
@@ -1,6 +1,7 @@
 package dev.fitko.fitconnect.api.domain.model.event.problems;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 
@@ -12,11 +13,19 @@ import lombok.Data;
 @Data
 @AllArgsConstructor
 public class Problem {
+
     @JsonIgnore
     public static final String SCHEMA_URL = "https://schema.fitko.de/fit-connect/events/problems/";
 
+    @JsonProperty("type")
     private final String type;
+
+    @JsonProperty("title")
     private final String title;
+
+    @JsonProperty("detail")
     private final String detail;
+
+    @JsonProperty("instance")
     private final String instance;
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionIssue.java
index 5408f7eca13945b83a7d325bd537a54c7c130f3f..409e25035c478785111915653bcb03cc8e13f887 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionIssue.java
@@ -2,16 +2,18 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.attachment;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
+import java.util.UUID;
+
 import static java.lang.String.format;
 
-public class AttachmentEncryptionIssue extends Problem {
+public final class AttachmentEncryptionIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Die Anlage %s konnte nicht entschlüsselt werden.";
-    private static final String instance = "attachment:%s";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Decryption failure";
+    private static final String DETAIL = "Decrypting attachment %s failed.";
+    private static final String INSTANCE = "attachment:%s";
 
-    public AttachmentEncryptionIssue(final String attachmentId) {
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public AttachmentEncryptionIssue(final UUID attachmentId) {
+        super(TYPE, TITLE, format(DETAIL, attachmentId), format(INSTANCE, attachmentId));
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionKeyIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionKeyIssue.java
index 1c8ec1d441ad87ceb860929a06a934ffaa6c30fe..00baff8bc62efd823e9846924a2de00a6cbf6ab7 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionKeyIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentEncryptionKeyIssue.java
@@ -4,14 +4,18 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
 import static java.lang.String.format;
 
-public class AttachmentEncryptionKeyIssue extends Problem {
+public final class AttachmentEncryptionKeyIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Der Schlüssel %s ist nicht der zu diesem Zweck vorgesehene Schlüssel.";
-    private static final String instance = "attachment:%s";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Decryption failure";
+    private static final String DETAIL = "The key %s is not the key intended for this purpose.";
+    private static final String INSTANCE = "attachment:%s";
 
     public AttachmentEncryptionKeyIssue(final String keyId) {
-        super(type, title, format(detail, keyId), format(instance, keyId));
+        super(TYPE, TITLE, format(DETAIL, keyId), format(INSTANCE, keyId));
+    }
+
+    public static Problem getWithAttachmentId(final String keyId) {
+        return new AttachmentEncryptionKeyIssue(keyId);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentHashMismatch.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentHashMismatch.java
index 744b4155ad374ef2252c0fdacd0c87481eda995f..eafa5058913fa74a1fcb2e2ccaa4e5ac8cc29524 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentHashMismatch.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/AttachmentHashMismatch.java
@@ -2,16 +2,18 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.attachment;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
+import java.util.UUID;
+
 import static java.lang.String.format;
 
-public class AttachmentHashMismatch extends Problem {
+public final class AttachmentHashMismatch extends Problem {
 
-    private static final String type = SCHEMA_URL + "hash-mismatch";
-    private static final String title = "Prüfsumme stimmt nicht";
-    private static final String detail = "Der Hash der Anlage %s stimmt nicht.";
-    private static final String instance = "attachment:%s";
+    private static final String TYPE = SCHEMA_URL + "hash-mismatch";
+    private static final String TITLE = "Checksum does not match";
+    private static final String DETAIL = "Attachment %s hash value is wrong.";
+    private static final String INSTANCE = "attachment:%s";
 
-    public AttachmentHashMismatch(final String attachmentId) {
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public AttachmentHashMismatch(final UUID attachmentId) {
+        super(TYPE, TITLE, format(DETAIL, attachmentId), format(INSTANCE, attachmentId));
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/IncorrectAttachmentAuthenticationTag.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/IncorrectAttachmentAuthenticationTag.java
index f0eefac5fd5fabd163e37f809d8cd398fda69993..68932328be2068faf6a3dd7de58bcc46988ebdad 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/IncorrectAttachmentAuthenticationTag.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/IncorrectAttachmentAuthenticationTag.java
@@ -2,16 +2,18 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.attachment;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
+import java.util.UUID;
+
 import static java.lang.String.format;
 
-public class IncorrectAttachmentAuthenticationTag extends Problem {
+public final class IncorrectAttachmentAuthenticationTag extends Problem {
 
-    private static final String type = SCHEMA_URL + "incorrect-authentication-tag";
-    private static final String title = "Authentication-Tag ungültig";
-    private static final String detail = "Das Authentication-Tag der Anlage %s ist ungültig.";
-    private static final String instance = "attachment:%s";
+    private static final String TYPE = SCHEMA_URL + "incorrect-authentication-tag";
+    private static final String TITLE = "Authentication tag is invalid";
+    private static final String DETAIL = "The authentication tag for the attachment %s is invalid.";
+    private static final String INSTANCE = "attachment:%s";
 
-    public IncorrectAttachmentAuthenticationTag(final String attachmentId) {
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public IncorrectAttachmentAuthenticationTag(final UUID attachmentId) {
+        super(TYPE, TITLE, format(DETAIL, attachmentId), format(INSTANCE, attachmentId));
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/InvalidAttachmentContent.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/InvalidAttachmentContent.java
index 8c81d1886e32f92f81b099988bcdc69082df7020..3436ae96ed0529b1eb82068dbf1ba909e18d848c 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/InvalidAttachmentContent.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/InvalidAttachmentContent.java
@@ -2,16 +2,19 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.attachment;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
+import java.util.UUID;
+
 import static java.lang.String.format;
 
-public class InvalidAttachmentContent extends Problem {
+public final class InvalidAttachmentContent extends Problem {
 
-    private static final String type = SCHEMA_URL + "invalid-content";
-    private static final String title = "Unzulässiger Inhalt";
-    private static final String detail = "Der Inhalt der Anlage %s ist nicht zulässig.";
-    private static final String instance = "attachment:%s";
+    private static final String TYPE = SCHEMA_URL + "invalid-content";
+    private static final String TITLE = "Invalid content";
+    private static final String DETAIL = "The content of the attachment %s is not allowed.";
+    private static final String INSTANCE = "attachment:%s";
 
-    public InvalidAttachmentContent(final String attachmentId) {
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public InvalidAttachmentContent(final UUID attachmentId) {
+        super(TYPE, TITLE, format(DETAIL, attachmentId), format(INSTANCE, attachmentId));
     }
+
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/MissingAttachment.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/MissingAttachment.java
index e1ed4ab551e5a588157468ff7cea1e54cc333dd4..7afdec4a55a2ce3d792ca41ac773fba8a1ec2451 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/MissingAttachment.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/attachment/MissingAttachment.java
@@ -2,15 +2,17 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.attachment;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
+import java.util.UUID;
+
 import static java.lang.String.format;
 
-public class MissingAttachment extends Problem {
-    private static final String type = SCHEMA_URL + "missing-attachment";
-    private static final String title =  "Anlage fehlt";
-    private static final String detail = "Die Anlage %s konnte nicht geladen werden.";
-    private static final String instance = "attachment:%s";
+public final class MissingAttachment extends Problem {
+    private static final String TYPE = SCHEMA_URL + "missing-attachment";
+    private static final String TITLE =  "List of attachments is invalid";
+    private static final String DETAIL = "Unable to load attachment %s.";
+    private static final String INSTANCE = "attachment:%s";
 
-    public MissingAttachment(final String attachmentId){
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public MissingAttachment(final UUID attachmentId){
+        super(TYPE, TITLE, format(DETAIL, attachmentId), format(INSTANCE, attachmentId));
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionIssue.java
index bea6ba5935ff385f147971b75b02578cbf56f8b8..39f5d6d088b5561e8a6c8be3aa5f41da441952c2 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionIssue.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class DataEncryptionIssue extends Problem {
+public final class DataEncryptionIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Der Fachdatensatz konnte nicht entschlüsselt werden.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Decryption failure";
+    private static final String DETAIL = "Decrypting submission data failed.";
+    private static final String INSTANCE = "data";
 
     public DataEncryptionIssue() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionKeyIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionKeyIssue.java
index fd9aad66d0730344e1ef4d6d42b3fe7cac481139..797083f1e01e2f6675c9e4ac69994730d68ef830 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionKeyIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataEncryptionKeyIssue.java
@@ -4,14 +4,14 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
 import static java.lang.String.format;
 
-public class DataEncryptionKeyIssue extends Problem {
+public final class DataEncryptionKeyIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Der Schlüssel %s ist nicht der zu diesem Zweck vorgesehene Schlüssel.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Decryption failure";
+    private static final String DETAIL = "The key %s is not the key intended for this purpose.";
+    private static final String INSTANCE = "data";
 
     public DataEncryptionKeyIssue(final String keyId) {
-        super(type, title, format(detail, keyId), instance);
+        super(TYPE, TITLE, format(DETAIL, keyId), INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataHashMismatch.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataHashMismatch.java
index 34959d7867a2eda8d612334051eeb86658153fbe..657035eefd012ce8fa4484e49a456bb51bdf319b 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataHashMismatch.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataHashMismatch.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class DataHashMismatch extends Problem {
+public final class DataHashMismatch extends Problem {
 
-    private static final String type = SCHEMA_URL + "hash-mismatch";
-    private static final String title = "Prüfsumme stimmt nicht";
-    private static final String detail = "Die Prüfsumme des Fachdatensatzes stimmt nicht.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "hash-mismatch";
+    private static final String TITLE = "Checksum does not match";
+    private static final String DETAIL = "Submission data checksum wrong.";
+    private static final String INSTANCE = "data";
 
     public DataHashMismatch() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataJsonSyntaxViolation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataJsonSyntaxViolation.java
index 3d3fde4724b0a23f4bec00450e1e2185cca66992..f2f15dd6682116124c0b0838e12563c2c6e9c23a 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataJsonSyntaxViolation.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataJsonSyntaxViolation.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class DataJsonSyntaxViolation extends Problem {
+public final class DataJsonSyntaxViolation extends Problem {
 
-    private static final String type = SCHEMA_URL + "syntax-violation";
-    private static final String title = "Syntax-Fehler";
-    private static final String detail = "Der Fachdatensatz ist kein valides JSON.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "syntax-violation";
+    private static final String TITLE = "Syntax violation";
+    private static final String DETAIL = "Submission data is no valid JSON.";
+    private static final String INSTANCE = "data";
 
     public DataJsonSyntaxViolation() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataSchemaViolation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataSchemaViolation.java
index 94d6c7613aebffcde6ec72d2064f5b9fdfa0a6b3..31632425e625951f74747cfb0294ffcee4a2b827 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataSchemaViolation.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataSchemaViolation.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class DataSchemaViolation extends Problem {
+public final class DataSchemaViolation extends Problem {
 
-    private static final String type = SCHEMA_URL + "schema-violation";
-    private static final String title = "Schema-Fehler";
-    private static final String detail = "Der Fachdatensatz ist nicht schema-valide.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "schema-violation";
+    private static final String TITLE = "Schema violation";
+    private static final String DETAIL = "Submission data does not comply to schema.";
+    private static final String INSTANCE = "data";
 
     public DataSchemaViolation() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataXmlSyntaxViolation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataXmlSyntaxViolation.java
index 4098f7dd8b1b76b4749e6af8b76a0d02b560444b..fc12f172786babeab49a91c6f13d73eb85f0b1e5 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataXmlSyntaxViolation.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/DataXmlSyntaxViolation.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class DataXmlSyntaxViolation extends Problem {
+public final class DataXmlSyntaxViolation extends Problem {
 
-    private static final String type = SCHEMA_URL + "syntax-violation";
-    private static final String title = "Syntax-Fehler";
-    private static final String detail = "Der Fachdatensatz ist kein valides XML.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "syntax-violation";
+    private static final String TITLE = "Syntax violation";
+    private static final String DETAIL = "Submission data is no valid XML.";
+    private static final String INSTANCE = "data";
 
     public DataXmlSyntaxViolation() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/IncorrectDataAuthenticationTag.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/IncorrectDataAuthenticationTag.java
index b63523fbcce783d4be34294b0792a833489e754c..463626e522050791211b9d3c52a6778d07939f92 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/IncorrectDataAuthenticationTag.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/data/IncorrectDataAuthenticationTag.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.data;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class IncorrectDataAuthenticationTag extends Problem {
+public final class IncorrectDataAuthenticationTag extends Problem {
 
-    private static final String type = SCHEMA_URL + "incorrect-authentication-tag";
-    private static final String title = "Authentication-Tag ungültig";
-    private static final String detail = "Das Authentication-Tag des Fachdatensatzes ist ungültig.";
-    private static final String instance = "data";
+    private static final String TYPE = SCHEMA_URL + "incorrect-authentication-tag";
+    private static final String TITLE = "Authentication tag is invalid";
+    private static final String DETAIL = "The authentication tag for the submission data is invalid.";
+    private static final String INSTANCE = "data";
 
     public IncorrectDataAuthenticationTag() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/AttachmentsMismatch.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/AttachmentsMismatch.java
index 09aea66ae80f45342c0c212609242db372ef46e1..94f1adfd0f75715a50dc785e86e78553546cd9b8 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/AttachmentsMismatch.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/AttachmentsMismatch.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class AttachmentsMismatch extends Problem {
+public final class AttachmentsMismatch extends Problem {
 
-    private static final String type = SCHEMA_URL + "attachments-mismatch";
-    private static final String title = "Fehlerhafte Anlagen-Liste";
-    private static final String detail = "Die Liste der Anlagen in Submission und Metadatensatz stimmt nicht überein.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "attachments-mismatch";
+    private static final String TITLE = "List of attachments is invalid";
+    private static final String DETAIL = "The list of attachments in the submission and metadata record do not match.";
+    private static final String INSTANCE = "metadata";
 
     public AttachmentsMismatch() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/IncorrectMetadataAuthenticationTag.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/IncorrectMetadataAuthenticationTag.java
index 83227eb987bf5bae4eee561f2fd27e3d0ff52f84..c73d65ea6b656346c2dd9834742d75df647f73e8 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/IncorrectMetadataAuthenticationTag.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/IncorrectMetadataAuthenticationTag.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class IncorrectMetadataAuthenticationTag extends Problem {
+public final class IncorrectMetadataAuthenticationTag extends Problem {
 
-    private static final String type = SCHEMA_URL + "incorrect-authentication-tag";
-    private static final String title = "Authentication-Tag ungültig";
-    private static final String detail = "Das Authentication-Tag des Metadatensatzes ist ungültig.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "incorrect-authentication-tag";
+    private static final String TITLE = "Authentication tag is invalid";
+    private static final String DETAIL = "The authentication tag for the metadata is invalid.";
+    private static final String INSTANCE = "metadata";
 
     public IncorrectMetadataAuthenticationTag() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionIssue.java
index a99b770938551ace51026b7e9bbf34589cb9a6c4..31a0a1dcbe9f67e6942906ea742c09f6726a3b86 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionIssue.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class MetadataEncryptionIssue extends Problem {
+public final class MetadataEncryptionIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Die Entschlüsselung des Metadatensatzes ist fehlgeschlagen.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Decryption failure";
+    private static final String DETAIL = "Decrypting metadata failed.";
+    private static final String INSTANCE = "metadata";
 
     public MetadataEncryptionIssue() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionKeyIssue.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionKeyIssue.java
index 9dcf99d70bd46b8a02e0490d985dc4348ad12820..c7d0791de281a64fd1198bbf5b0b18ce1aec599e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionKeyIssue.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataEncryptionKeyIssue.java
@@ -4,14 +4,14 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
 import static java.lang.String.format;
 
-public class MetadataEncryptionKeyIssue extends Problem {
+public final class MetadataEncryptionKeyIssue extends Problem {
 
-    private static final String type = SCHEMA_URL + "encryption-issue";
-    private static final String title = "Entschlüsselungs-Fehler";
-    private static final String detail = "Der Schlüssel %s ist nicht der zu diesem Zweck vorgesehene Schlüssel.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "encryption-issue";
+    private static final String TITLE = "Encryption failure";
+    private static final String DETAIL = "The key %s is not the key intended for this purpose.";
+    private static final String INSTANCE = "metadata";
 
     public MetadataEncryptionKeyIssue(final String keyId) {
-        super(type, title, format(detail, keyId), instance);
+        super(TYPE, TITLE, format(DETAIL, keyId), INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataJsonSyntaxViolation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataJsonSyntaxViolation.java
index 728470dd28c09b1d694e6f2b18c6ce6849c3c5f9..566839a387cf05e59ea849be042a41fff762fd3e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataJsonSyntaxViolation.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataJsonSyntaxViolation.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class MetadataJsonSyntaxViolation extends Problem {
+public final class MetadataJsonSyntaxViolation extends Problem {
 
-    private static final String type = SCHEMA_URL + "syntax-violation";
-    private static final String title = "Syntax-Fehler";
-    private static final String detail = "Der Metadatensatz ist kein valides JSON.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "syntax-violation";
+    private static final String TITLE = "Syntax violation";
+    private static final String DETAIL = "Metadata record is no valid JSON.";
+    private static final String INSTANCE = "metadata";
 
     public MetadataJsonSyntaxViolation() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataSchemaViolation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataSchemaViolation.java
index 4a174f8be375aa60f82671463d2dae47373c0e42..636b572909e3cee712a178edbae042e4b594a626 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataSchemaViolation.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MetadataSchemaViolation.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class MetadataSchemaViolation extends Problem {
+public final class MetadataSchemaViolation extends Problem {
 
-    private static final String type = SCHEMA_URL + "schema-violation";
-    private static final String title = "Schema-Fehler";
-    private static final String detail = "Der Metadatensatz ist nicht schema-valide.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "schema-violation";
+    private static final String TITLE = "Schema violation";
+    private static final String DETAIL = "Submission data does not comply to schema.";
+    private static final String INSTANCE = "metadata";
 
     public MetadataSchemaViolation() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingData.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingData.java
index ba390303a6ecff2003c5ee83a14f01a5a65b39fb..35fda0bcc283b894d441b2444d6fa242b50d2d10 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingData.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingData.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class MissingData extends Problem {
+public final class MissingData extends Problem {
 
-    private static final String type = SCHEMA_URL + "missing-data";
-    private static final String title = "Fachdatensatz fehlt";
-    private static final String detail = "Der Fachdatensatz fehlt.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "missing-data";
+    private static final String TITLE = "Submission data missing";
+    private static final String DETAIL = "Submission data is missing";
+    private static final String INSTANCE = "metadata";
 
     public MissingData() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingSchemaReference.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingSchemaReference.java
index 17b5ec37589e6ce9853f89e9eef24db43a2afa2f..960a0d232f30d96ed9c0040f1d8b96cd007ffa40 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingSchemaReference.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/MissingSchemaReference.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class MissingSchemaReference extends Problem {
+public final class MissingSchemaReference extends Problem {
 
-    private static final String type = SCHEMA_URL + "missing-schema";
-    private static final String title = "Schema-Referenz fehlt";
-    private static final String detail = "Die Schema-Referenz fehlt im Metadatensatz.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "missing-schema";
+    private static final String TITLE = "Schema reference missing";
+    private static final String DETAIL = "Metadata does not contain a schema reference.";
+    private static final String INSTANCE = "metadata";
 
     public MissingSchemaReference() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/ServiceMismatch.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/ServiceMismatch.java
index 9deae15efd19823f405d7342aefcbbd14960c6ac..046d6356b45c68480a76fc7389cad321db87fddd 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/ServiceMismatch.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/ServiceMismatch.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class ServiceMismatch extends Problem {
+public final class ServiceMismatch extends Problem {
 
-    private static final String type = SCHEMA_URL + "service-mismatch";
-    private static final String title = "Verwaltungsleistung stimmt nicht überein";
-    private static final String detail = "Die Verwaltungsleistung in Submission und Metadatensatz stimmen nicht überein.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "service-mismatch";
+    private static final String TITLE = "Service type does not match";
+    private static final String DETAIL = "Service type of metadata does not match submission.";
+    private static final String INSTANCE = "metadata";
 
     public ServiceMismatch() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedDataSchema.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedDataSchema.java
index ccd19f355db46fad054d3540f3232b182122201f..b4951e47fae96b8ab7b7ef921a65c8f7c37ca60f 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedDataSchema.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedDataSchema.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class UnsupportedDataSchema extends Problem {
+public final class UnsupportedDataSchema extends Problem {
 
-    private static final String type = SCHEMA_URL + "unsupported-schema";
-    private static final String title = "Fachdatenschema nicht unterstützt";
-    private static final String detail = "Das angegebene Fachdatenschema wird nicht unterstützt.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "unsupported-schema";
+    private static final String TITLE = "Data schema not supported";
+    private static final String DETAIL = "Submission data schema is not supported.";
+    private static final String INSTANCE = "metadata";
 
     public UnsupportedDataSchema() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedMetadataSchema.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedMetadataSchema.java
index 29bfc120d398c1f9cd5ac6dbe9ceed3b6d026577..752dd33c476523bdfda20ecf2b939b35983e1250 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedMetadataSchema.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedMetadataSchema.java
@@ -4,14 +4,14 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
 import static java.lang.String.format;
 
-public class UnsupportedMetadataSchema extends Problem {
+public final class UnsupportedMetadataSchema extends Problem {
 
-    private static final String type = SCHEMA_URL + "unsupported-schema";
-    private static final String title = "Metadatenschema nicht unterstützt";
-    private static final String detail = "Die angegebene Metadatenschema-URI ('%s') ist keines der unterstützten Metadatenschemas.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "unsupported-schema";
+    private static final String TITLE = "Metadata schema not supported";
+    private static final String DETAIL = "The specified metadata schema URI ('%s') is not referring to a supported metadata schema.";
+    private static final String INSTANCE = "metadata";
 
     public UnsupportedMetadataSchema(final String metadataSchemaUri) {
-        super(type, title, format(detail, metadataSchemaUri), instance);
+        super(TYPE, TITLE, format(DETAIL, metadataSchemaUri), INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedReplyChannel.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedReplyChannel.java
index d13246a96340a2658f3ee0487797277b71239632..7eeb1a5d2f4c641f9d79d402e1955653426fabe9 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedReplyChannel.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedReplyChannel.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class UnsupportedReplyChannel extends Problem {
+public final class UnsupportedReplyChannel extends Problem {
 
-    private static final String type = SCHEMA_URL + "unsupported-reply-channel";
-    private static final String title = "Rückkanal nicht unterstützt";
-    private static final String detail = "Der gewählte Rückkanal wird nicht unterstützt.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "unsupported-reply-channel";
+    private static final String TITLE = "Reply channel is not supported";
+    private static final String DETAIL = "The chosen reply channel is not supported.";
+    private static final String INSTANCE = "metadata";
 
     public UnsupportedReplyChannel() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedService.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedService.java
index e7744a14ebf5e3e2a47d14926423303ea0fdcbc9..c6994df90fc010ffe5236cc5b3afa194746e2b51 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/metadata/UnsupportedService.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.metadata;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class UnsupportedService extends Problem {
+public final class UnsupportedService extends Problem {
 
-    private static final String type = SCHEMA_URL + "unsupported-service";
-    private static final String title = "Verwaltungsleistung nicht unterstützt";
-    private static final String detail = "Die angegebene Verwaltungsleistung wird nicht unterstützt.";
-    private static final String instance = "metadata";
+    private static final String TYPE = SCHEMA_URL + "unsupported-service";
+    private static final String TITLE = "Service nor supported";
+    private static final String DETAIL = "The specified service is not supported.";
+    private static final String INSTANCE = "metadata";
 
     public UnsupportedService() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/other/TechnicalError.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/other/TechnicalError.java
index 344d855d11d659982e87a1ab8a6faaf83e838e05..b947172136a789ec8a181c8400ac3f38c6ed6154 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/other/TechnicalError.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/other/TechnicalError.java
@@ -2,14 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.other;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class TechnicalError extends Problem {
+public final class TechnicalError extends Problem {
 
-    private static final String type = SCHEMA_URL + "technical-error";
-    private static final String title = "Technischer Fehler";
-    private static final String detail = "Bei der Verarbeitung im empfangenden System trat ein technischer Fehler auf.";
-    private static final String instance = "other";
+    private static final String TYPE = SCHEMA_URL + "technical-error";
+    private static final String TITLE = "Technical error";
+    private static final String DETAIL = "A technical error occurred during processing in the receiving system.";
+    private static final String INSTANCE = "other";
 
     public TechnicalError() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/AttachmentsMismatch.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/AttachmentsMismatch.java
index 9fc8a745ef9639fe70daf552b4732c3d9c566b58..2c5cccc6c6d7a16e91083419a93c593117297619 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/AttachmentsMismatch.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/AttachmentsMismatch.java
@@ -2,13 +2,13 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.submission;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class AttachmentsMismatch extends Problem {
-    private static final String type = SCHEMA_URL + "attachments-mismatch";
-    private static final String title = "Fehlerhafte Anlagen-Liste";
-    private static final String detail = "Die Liste der Anlagen in Submission und Event-Log stimmt nicht überein.";
-    private static final String instance = "submission";
+public final class AttachmentsMismatch extends Problem {
+    private static final String TYPE = SCHEMA_URL + "attachments-mismatch";
+    private static final String TITLE = "List of attachments is invalid";
+    private static final String DETAIL = "List of attachments in submission does not match list on event log.";
+    private static final String INSTANCE = "submission";
 
     public AttachmentsMismatch() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/InvalidEventLog.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/InvalidEventLog.java
index 34afda8b034db2911505bd4e2d0a1bb9a0f03a87..94996b1c29da4d0f5ebc893dee2e304867f49d07 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/InvalidEventLog.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/InvalidEventLog.java
@@ -2,13 +2,14 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.submission;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class InvalidEventLog extends Problem {
-    private static final String type = SCHEMA_URL + "invalid-event-log";
-    private static final String title = "Inkonsistentes Event-Log";
-    private static final String detail = "Das Event-Log ist inkonsistent.";
-    private static final String instance = "submission";
+public final class InvalidEventLog extends Problem {
+
+    private static final String TYPE = SCHEMA_URL + "invalid-event-log";
+    private static final String TITLE = "Inconsistent Event-Log";
+    private static final String DETAIL = "The Event-Log is inconsistent.";
+    private static final String INSTANCE = "submission";
 
     public InvalidEventLog() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/MissingAuthenticationTags.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/MissingAuthenticationTags.java
index c63029458d64288e7696291af7cc7a0936b0cc6f..ef1940f737b3e784deb6544bc5b922e5dd0597ac 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/MissingAuthenticationTags.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/MissingAuthenticationTags.java
@@ -2,15 +2,13 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.submission;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-import static java.lang.String.format;
-
 public class MissingAuthenticationTags extends Problem {
-    private static final String type = SCHEMA_URL + "missing-authentication-tags";
-    private static final String title = "Fehlende Authentication-Tags";
-    private static final String detail = "Das Event 'submit-submission' enthält keine Authentication-Tags.";
-    private static final String instance = "submission";
+    private static final String TYPE = SCHEMA_URL + "missing-authentication-tags";
+    private static final String TITLE = "Authentication tags missing";
+    private static final String DETAIL = "The 'submit-submission' event does not contain authentication tags.";
+    private static final String INSTANCE = "submission";
 
-    public MissingAuthenticationTags(final String attachmentId){
-        super(type, title, format(detail, attachmentId), format(instance, attachmentId));
+    public MissingAuthenticationTags(){
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/NotExactlyOneSubmitEvent.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/NotExactlyOneSubmitEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..19c4c48bf36338d989407f350f85f49520dcca24
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/NotExactlyOneSubmitEvent.java
@@ -0,0 +1,16 @@
+package dev.fitko.fitconnect.api.domain.model.event.problems.submission;
+
+import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+
+public class NotExactlyOneSubmitEvent extends Problem {
+
+    private static final String TYPE = SCHEMA_URL + "invalid-event-log";
+    private static final String TITLE = "Inconsistent Event-Log";
+    private static final String DETAIL = "The Event-Log is inconsistent because it does not contain exactly one 'submit' event.";
+    private static final String INSTANCE = "submission";
+
+    public NotExactlyOneSubmitEvent() {
+        super(TYPE, TITLE, DETAIL, INSTANCE);
+    }
+
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/SubmissionTimeout.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/SubmissionTimeout.java
index deaa8eb8145e36e99b8e00ec7f26d86d4289dc2a..1d690d87e27e3b4857a55277d369669c37e5786a 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/SubmissionTimeout.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/problems/submission/SubmissionTimeout.java
@@ -2,13 +2,13 @@ package dev.fitko.fitconnect.api.domain.model.event.problems.submission;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 
-public class SubmissionTimeout extends Problem {
-    private static final String type = SCHEMA_URL + "timeout";
-    private static final String title = "Zeitablauf";
-    private static final String detail = "Die Einreichung wurde automatisch zurückgewiesen.";
-    private static final String instance = "submission";
+public final class SubmissionTimeout extends Problem {
+    private static final String TYPE = SCHEMA_URL + "timeout";
+    private static final String TITLE = "Submission Timeout";
+    private static final String DETAIL = "The submission was automatically rejected.";
+    private static final String INSTANCE = "submission";
 
     public SubmissionTimeout() {
-        super(type, title, detail, instance);
+        super(TYPE, TITLE, DETAIL, INSTANCE);
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/Metadata.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/Metadata.java
index 70fc92285652a6303b550a133af0cf516fba9ab9..ce940361d9e4b4ff200162501262273587bd299e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/Metadata.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/Metadata.java
@@ -5,38 +5,31 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import dev.fitko.fitconnect.api.domain.model.metadata.payment.PaymentInformation;
 import dev.fitko.fitconnect.api.domain.model.replychannel.ReplyChannel;
 import lombok.AllArgsConstructor;
+import lombok.Data;
 import lombok.EqualsAndHashCode;
-import lombok.Getter;
 import lombok.NoArgsConstructor;
-import lombok.Setter;
 
 import java.util.List;
 
-@Setter
+@Data
 @EqualsAndHashCode
 @AllArgsConstructor
 @NoArgsConstructor
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class Metadata {
 
-    @Getter
     @JsonProperty("$schema")
     private String schema;
 
-    @Getter
     private ContentStructure contentStructure;
 
     private PublicServiceType publicServiceType;
 
-    @Getter
     private List<AuthenticationInformation> authenticationInformation;
 
-    @Getter
     private PaymentInformation paymentInformation;
 
-    @Getter
     private ReplyChannel replyChannel;
 
-    @Getter
     private AdditionalReferenceInfo additionalReferenceInfo;
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/attachment/AttachmentForValidation.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/attachment/AttachmentForValidation.java
new file mode 100644
index 0000000000000000000000000000000000000000..8fc92512872a5ad643559b6b0b31030734cdf1a9
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/metadata/attachment/AttachmentForValidation.java
@@ -0,0 +1,25 @@
+package dev.fitko.fitconnect.api.domain.model.metadata.attachment;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.UUID;
+
+@Data
+@AllArgsConstructor
+public class AttachmentForValidation {
+
+    private UUID attachmentId;
+    private String metadataHash;
+    private byte[] decryptedData;
+    private String encryptedData;
+    private ApiAttachment attachmentMetadata;
+
+    public AttachmentForValidation(final ApiAttachment attachmentMetadata, final String encryptedData, final byte[] decryptedData){
+        attachmentId = attachmentMetadata.getAttachmentId();
+        metadataHash = attachmentMetadata.getHash().getContent();
+        this.attachmentMetadata = attachmentMetadata;
+        this.decryptedData = decryptedData;
+        this.encryptedData = encryptedData;
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
index 456555ea39494dd7dc190a1d70d4766f80551565..7f59e8f0217aaca96bc107cb26d976e17357de15 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
@@ -39,7 +39,7 @@ public class ValidationContext {
         }
     }
 
-    public void addResult(final boolean test, final String message) {
+    public void addErrorIfTestFailed(final boolean test, final String message) {
         if (!test) {
             addResult(ValidationResult.error(new ValidationException(message)));
         }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
index 36cd9fd31a656701b2d68210db4eeb1561ba5caa..129780e15ceb875ba1a9538599e2ccd3716bd26a 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
@@ -1,39 +1,135 @@
 package dev.fitko.fitconnect.api.domain.validation;
 
+import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Wrapper for validations including an exception
  */
 public final class ValidationResult {
 
     private final boolean isValid;
-    private Exception error;
+    private final Exception error;
+
+    private final List<Problem> validationProblems = new ArrayList<>();
 
     private ValidationResult(final boolean isValid) {
         this.isValid = isValid;
+        error = null;
     }
 
-    private ValidationResult(final boolean isValid, final Exception exception) {
+    private ValidationResult(final boolean isValid, final Exception error) {
         this.isValid = isValid;
-        this.error = exception;
+        this.error = error;
+    }
+
+    private ValidationResult(final Problem problem) {
+        isValid = false;
+        error = null;
+        validationProblems.add(problem);
     }
 
+    private ValidationResult(final List<Problem> problems) {
+        isValid = false;
+        error = null;
+        validationProblems.addAll(problems);
+    }
+
+    private ValidationResult(final Exception error, final Problem problem) {
+        isValid = false;
+        this.error = error;
+        validationProblems.add(problem);
+    }
+
+    /**
+     * Create new valid result.
+     *
+     * @return the valid result
+     */
     public static ValidationResult ok() {
         return new ValidationResult(true);
     }
 
+    /**
+     * Create new failed result with an exception.
+     *
+     * @return the invalid result
+     */
     public static ValidationResult error(final Exception exception) {
         return new ValidationResult(false, exception);
     }
 
+    /**
+     * Create new failed result with a {@link Problem}.
+     *
+     * @return the invalid result
+     */
+    public static ValidationResult problem(final Problem problem) {
+        return new ValidationResult(problem);
+    }
+
+    /**
+     * Create new failed result with a list of {@link Problem}.
+     *
+     * @return the invalid result
+     */
+    public static ValidationResult problems(final List<Problem> problems) {
+        return new ValidationResult(problems);
+    }
+
+    /**
+     * Create new failed result with an Exception and a {@link Problem}.
+     *
+     * @return the invalid result
+     */
+    public static ValidationResult withErrorAndProblem(final Exception exception, final Problem problem) {
+        return new ValidationResult(exception, problem);
+    }
+
+    /**
+     * Successful validation without errors.
+     *
+     * @return true if valid
+     */
     public boolean isValid() {
-        return this.isValid;
+        return isValid;
     }
 
+    /**
+     * Failed validation with an error.
+     *
+     * @return true if an error occurred
+     */
     public boolean hasError() {
-        return !this.isValid || this.error != null;
+        return error != null;
+    }
+
+    /**
+     * Failed validation with a problem error that gets auto-rejected.
+     *
+     * @return true if a problem occurred
+     */
+    public boolean hasProblems() {
+        return !validationProblems.isEmpty();
+    }
+
+    /**
+     * Gets the problem that was detected during validation.
+     *
+     * @return {@link Problem}
+     */
+    public List<Problem> getProblems() {
+        return validationProblems;
     }
 
+    /**
+     * Gets the exception that occurred during validation.
+     *
+     * @return {@link Exception}
+     */
     public Exception getError() {
-        return this.error;
+        return error;
     }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b4ad53aa037dc3cde571b77dde849582a952d83
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java
@@ -0,0 +1,12 @@
+package dev.fitko.fitconnect.api.exceptions;
+
+public class AuthenticationTagsEmptyException extends RuntimeException {
+
+    public AuthenticationTagsEmptyException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+
+    public AuthenticationTagsEmptyException(final String errorMessage) {
+        super(errorMessage);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java
index 6fdfc7cd3567d182f658a81c1d9add446743ca1a..f745958c039fd7d0d1004aec734b5f178bd72fc3 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java
@@ -1,7 +1,13 @@
 package dev.fitko.fitconnect.api.exceptions;
 
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
 public class RestApiException extends RuntimeException {
 
+    @Getter
+    private HttpStatus httpStatus;
+
     public RestApiException(final String errorMessage, final Throwable error) {
         super(errorMessage, error);
     }
@@ -9,4 +15,9 @@ public class RestApiException extends RuntimeException {
     public RestApiException(final String errorMessage) {
         super(errorMessage);
     }
+
+    public RestApiException(final HttpStatus httpStatus, final String errorMessage) {
+        super(errorMessage);
+        this.httpStatus = httpStatus;
+    }
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java
new file mode 100644
index 0000000000000000000000000000000000000000..708c3355cbee2f243e09dd4f6771d37d8f48fe83
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java
@@ -0,0 +1,12 @@
+package dev.fitko.fitconnect.api.exceptions;
+
+public class SubmissionRequestException extends RuntimeException {
+
+    public SubmissionRequestException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+
+    public SubmissionRequestException(final String errorMessage) {
+        super(errorMessage);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..5075c4886298d75067ea9de71fcef7348c1a89c0
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java
@@ -0,0 +1,12 @@
+package dev.fitko.fitconnect.api.exceptions;
+
+public class SubmitEventNotFoundException extends RuntimeException {
+
+    public SubmitEventNotFoundException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+
+    public SubmitEventNotFoundException(final String errorMessage) {
+        super(errorMessage);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
index 63ce5c0755475a1d74aa11c0446676dda3d8915b..d7ce15ff790f1de2d350bfa0132a7239ce5559e0 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
@@ -28,21 +28,6 @@ import java.util.UUID;
  */
 public interface Sender {
 
-    /**
-     * Validates the public key consisting of the following steps:
-     * <p>
-     * <ul>
-     *     <li>checks if the JSON Web Key is suitable for the encryption</li>
-     *     <li>checks if the public key is matching the certificate referenced in the JWK</li>
-     *     <li>checks the certificate chain up to the root certificate</li>
-     *     <li>checks against a certificate revocation list and/or an OSCP-endpoint with signed response</li>
-     * </ul>
-     * @param publicKey the public JWK
-     *
-     * @return {@link ValidationResult} that includes an error if the validation failed
-     */
-    ValidationResult validatePublicKey(RSAKey publicKey);
-
     /**
      * Validates the {@link Metadata} structure against a given JSON-schema to ensure its correctness.
      *
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java b/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
index 3c612b212cc194c8a1536d7c4d6cda31fffd617e..946fed1a5daaaf43de0c92f98b37fb90c323e716 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
@@ -2,14 +2,19 @@ package dev.fitko.fitconnect.api.services;
 
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
 import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.RestApiException;
 
 import java.util.List;
 import java.util.Set;
@@ -29,7 +34,7 @@ public interface Subscriber {
      * @param encryptedContent JWE encrypted content that should be decrypted
      * @return the decrypted content as byte[]
      */
-    byte[] decryptStringContent(RSAKey privateKey, String encryptedContent);
+    byte[] decryptStringContent(RSAKey privateKey, String encryptedContent) throws DecryptionException;
 
     /**
      * Polls available {@link SubmissionForPickup}s for a given destinationId.
@@ -39,7 +44,7 @@ public interface Subscriber {
      * @param offset        position in the dataset
      * @return list of found {@link SubmissionForPickup}s
      */
-    Set<SubmissionForPickup> pollAvailableSubmissionsForDestination(UUID destinationId, int offset, int limit);
+    Set<SubmissionForPickup> pollAvailableSubmissionsForDestination(UUID destinationId, int offset, int limit) throws RestApiException;
 
     /**
      * Polls available {@link SubmissionForPickup}s for all destinations the {@link Subscriber} is assigned to.
@@ -56,7 +61,7 @@ public interface Subscriber {
      * @param submissionId the unique identifier of a {@link Submission}
      * @return the requested {@link Submission}
      */
-    Submission getSubmission(UUID submissionId);
+    Submission getSubmission(UUID submissionId) throws RestApiException;
 
     /**
      * Loads encrypted {@link ApiAttachment} for a {@link Submission}.
@@ -65,7 +70,7 @@ public interface Subscriber {
      * @param attachmentId unique identifier of the attachments
      * @return encrypted JWE string of attachment
      */
-    String fetchAttachment(UUID submissionId, UUID attachmentId);
+    String fetchAttachment(UUID submissionId, UUID attachmentId) throws RestApiException;
 
     /**
      * Retrieve the entire event log for a submissions caseId of a specific destination.
@@ -74,15 +79,38 @@ public interface Subscriber {
      * @param destinationId unique identifier of the {@link Destination} the log should be retrieved for
      * @return List of {@link EventLogEntry}s for the given case
      */
-    List<EventLogEntry> getEventLog(UUID caseId, UUID destinationId);
+    List<EventLogEntry> getEventLog(UUID caseId, UUID destinationId) throws RestApiException;
 
     /**
-     * Validates the {@link Metadata} structure against a given JSON-schema to ensure its correctness.
+     * Validates the {@link Metadata} structure and contents to ensure its correctness.
      *
      * @param metadata the {@link Metadata} object that is validated
-     * @return a {@link ValidationResult}, contains an error if the {@link Metadata} is invalid or doesn't match the schema
+     * @param submission the {@link Submission} of the validated metadata
+     * @param authenticationTags the {@link AuthenticationTags} of the validated metadata
+     * @return a {@link ValidationResult}, contains an error if the {@link Metadata} is invalid, e.g. doesn't match the schema
      */
-    ValidationResult validateMetadata(Metadata metadata);
+    ValidationResult validateMetadata(Metadata metadata, Submission submission, AuthenticationTags authenticationTags);
+
+    /**
+     * Validates the attachment structure and contents to ensure its correctness.
+     *
+     * @param attachmentsForValidation list of attachments containing the hash, decrypted and encrypted data needed for validation
+     * @param authenticationTags the {@link AuthenticationTags} of the validated attachments
+
+     * @return a {@link ValidationResult}, contains an error if the attachment is invalid
+     */
+    ValidationResult validateAttachments(List<AttachmentForValidation> attachmentsForValidation, AuthenticationTags authenticationTags);
+
+    /**
+     * Validates the {@link Data} structure and contents to ensure its correctness.
+     *
+     * @param data               the unencrypted data as byte[]
+     * @param submission         the {@link Submission} of the validated data
+     * @param metadata           the {@link Metadata} of the validated data
+     * @param authenticationTags the {@link AuthenticationTags} of the validated data
+     * @return a {@link ValidationResult}, contains an error if the {@link Metadata} is invalid, e.g. doesn't match the schema
+     */
+    ValidationResult validateData(byte[] data, Submission submission, Metadata metadata, AuthenticationTags authenticationTags);
 
     /**
      * Validates data integrity of {@link ApiAttachment} or {@link Data}.
@@ -119,4 +147,13 @@ public interface Subscriber {
      * @see <a href="https://docs.fitko.de/fit-connect/docs/receiving/process-and-acknowledge">Process And Acknowledge</a>
      */
     void rejectSubmission(EventPayload eventPayload);
+
+    /**
+     * Get event from event-log filtered by a given event and submission.
+     *
+     * @param event event type to filter the event-log by
+     * @param submission submission to filter the event-log by
+     * @return {@link AuthenticationTags} of metadata, data and attachments
+     */
+    AuthenticationTags getAuthenticationTagsForEvent(Event event, Submission submission);
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
index bef075653ed6bca6d95aa9a09a56438696f00aa1..095dfa3982a1c4d1b46a1a10573f3b4afc8a8e57 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
@@ -1,13 +1,13 @@
 package dev.fitko.fitconnect.api.services.events;
 
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLog;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
 
 import java.util.List;
 import java.util.UUID;
@@ -27,8 +27,9 @@ public interface EventLogService {
      * @param caseId unique case identifier
      * @param destinationId unique identifier of the destination
      * @return list of {@link EventLogEntry}
+     * @throws EventLogException if a technical error occurred or the validation failed
      */
-    List<EventLogEntry> getEventLog(UUID caseId, UUID destinationId) throws RestApiException, EventLogException;
+    List<EventLogEntry> getEventLog(UUID caseId, UUID destinationId) throws EventLogException;
 
     /**
      * Retrieve the current status of a {@link Submission}.
@@ -37,16 +38,27 @@ public interface EventLogService {
      * @param caseId unique identifier of the case the log should be retrieved for
      * @param submissionId unique identifier of the submission the log should be retrieved for
      * @param authenticationTags {@link AuthenticationTags} used for SET-Event integrity validation
-     *
      * @return {@link SubmissionStatus} the current status
+     * @throws EventLogException if a technical error occurred or the validation failed
      */
-    SubmissionStatus getLastedEvent(UUID destinationId, UUID caseId, UUID submissionId, AuthenticationTags authenticationTags) throws RestApiException, EventLogException;
+    SubmissionStatus getLastedEvent(UUID destinationId, UUID caseId, UUID submissionId, AuthenticationTags authenticationTags) throws EventLogException;
 
     /**
      * Send an event for a given caseId.
      *
      * @param caseId unique case identifier
      * @param signedAndSerializedSET the serialised and signed event as SET string
+     * @throws EventLogException if a technical error occurred
+     */
+    void sendEvent(UUID caseId, String signedAndSerializedSET) throws EventLogException;
+
+    /**
+     * Get authentication tags for a given event and caseId.
+     *
+     * @param event the event type to filter the log for
+     * @param submission submission data
+     * @return {@link AuthenticationTags} for metadata, data and attachments
+     * @throws EventLogException if a technical error occurred or the validation failed
      */
-    void sendEvent(UUID caseId, String signedAndSerializedSET) throws RestApiException;
+    AuthenticationTags getAuthenticationTagsForEvent(Event event, Submission submission) throws EventLogException;
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/keys/KeyService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/keys/KeyService.java
index 040dc4166d8c5b001ff46a63f5ac566f432e11c6..e627b3cefa2fa1fe9e3131672e017ea7d653c3af 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/keys/KeyService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/keys/KeyService.java
@@ -1,5 +1,6 @@
 package dev.fitko.fitconnect.api.services.keys;
 
+import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
@@ -19,7 +20,7 @@ public interface KeyService {
      *
      * @param destinationId unique identifier of the {@link Destination}
      *
-     * @return validated {@link RSAKey} (@see {@link ValidationService#validateEncryptionPublicKey(RSAKey)})
+     * @return validated {@link RSAKey} (@see {@link ValidationService#validatePublicKey(RSAKey, KeyOperation)}})
      */
     RSAKey getPublicEncryptionKey(UUID destinationId);
 
@@ -28,7 +29,7 @@ public interface KeyService {
      *
      * @param destinationId unique identifier of the {@link Destination}
      * @param keyId unique identifier of the {@link RSAKey}
-     * @return validated {@link RSAKey} (@see {@link ValidationService#validateEncryptionPublicKey(RSAKey)})
+     * @return validated {@link RSAKey} (@see {@link ValidationService#validatePublicKey(RSAKey, KeyOperation)}})
      */
     RSAKey getPublicSignatureKey(UUID destinationId, String keyId);
 
@@ -36,7 +37,7 @@ public interface KeyService {
      * Get a public signature key for a given key-id from the self-service portal well-known keys.
      *
      * @param keyId unique identifier of the {@link RSAKey}
-     * @return validated {@link RSAKey} (@see {@link ValidationService#validateEncryptionPublicKey(RSAKey)})
+     * @return validated {@link RSAKey} (@see {@link ValidationService#validatePublicKey(RSAKey, KeyOperation)}})
      */
     RSAKey getPortalPublicKey(String keyId);
 
@@ -44,7 +45,7 @@ public interface KeyService {
      * Get a public signature key for a given key-id from the submission service well-known keys.
      *
      * @param keyId unique identifier of the {@link RSAKey}
-     * @return validated {@link RSAKey} (@see {@link ValidationService#validateEncryptionPublicKey(RSAKey)})
+     * @return validated {@link RSAKey} (@see {@link ValidationService#validatePublicKey(RSAKey, KeyOperation)}})
      */
     RSAKey getSubmissionServicePublicKey(String keyId);
 
@@ -54,7 +55,7 @@ public interface KeyService {
      *
      * @param url custom url to load the well known keys from
      * @param keyId unique identifier of the {@link RSAKey} the well known keys are filtered by
-     * @return validated {@link RSAKey} (@see {@link ValidationService#validateEncryptionPublicKey(RSAKey)})
+     * @return validated {@link RSAKey} (@see {@link ValidationService#validatePublicKey(RSAKey, KeyOperation)}})
      */
     RSAKey getWellKnownKeysForSubmissionUrl(String url, String keyId);
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
index 2256452c8c5e9520519e8dd4bccd4d473628f675..5f49f41da3c6b6bff5c30ef75f3bf1f5e3c1308e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
@@ -30,7 +30,7 @@ public interface SubmissionService {
      *
      * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/start-submission">Announcing a submission</a>
      */
-    SubmissionForPickup announceSubmission(CreateSubmission submission);
+    SubmissionForPickup announceSubmission(CreateSubmission submission) throws RestApiException;
 
     /**
      * Send a submission that was already {@link #announceSubmission(CreateSubmission) announced} before.
@@ -38,7 +38,7 @@ public interface SubmissionService {
      * @param submission submission including the encrypted {@link Data} and {@link Metadata}
      * @return the submission that was sent
      */
-    Submission sendSubmission(SubmitSubmission submission);
+    Submission sendSubmission(SubmitSubmission submission) throws RestApiException;
 
     /**
      * Get a {@link Submission} by id.
@@ -46,7 +46,7 @@ public interface SubmissionService {
      * @param submissionId unique submission identifier
      * @return submission matching the given id
      */
-    Submission getSubmission(UUID submissionId);
+    Submission getSubmission(UUID submissionId) throws RestApiException;
 
     /**
      * Get all available submissions.
@@ -65,7 +65,7 @@ public interface SubmissionService {
      * @param limit number of submissions in result (max. is 500)
      * @return SubmissionsForPickup containing a list of submissions
      */
-    SubmissionsForPickup pollAvailableSubmissionsForDestination(UUID destinationId, int offset, int limit);
+    SubmissionsForPickup pollAvailableSubmissionsForDestination(UUID destinationId, int offset, int limit) throws RestApiException;
 
     /**
      * Upload an encrypted {@link ApiAttachment}.
@@ -74,7 +74,7 @@ public interface SubmissionService {
      * @param attachmentId unique destination identifier
      * @param encryptedAttachment JWE encrypted attachment payload
      */
-    void uploadAttachment(UUID submissionId, UUID attachmentId, String encryptedAttachment);
+    void uploadAttachment(UUID submissionId, UUID attachmentId, String encryptedAttachment) throws RestApiException;
 
     /**
      * Get an {@link ApiAttachment} by id for a given {@link Submission}.
@@ -83,7 +83,7 @@ public interface SubmissionService {
      * @param attachmentId unique attachment identifier
      * @return encrypted string of the attachment data
      */
-    String getAttachment(UUID submissionId, UUID attachmentId);
+    String getAttachment(UUID submissionId, UUID attachmentId) throws RestApiException;
 
     /**
      * Get the submissions {@link Destination} by id.
@@ -93,5 +93,5 @@ public interface SubmissionService {
      *
      * @throws RestApiException if an error occurred
      */
-    Destination getDestination(UUID destinationID);
+    Destination getDestination(UUID destinationID) throws RestApiException;
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
index c5d747b3f9e54e225c6f9ef0d55aad86628c4f4d..8941384a1f953f6fdd0d2cf2c1b8fe0f356278e8 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/validation/ValidationService.java
@@ -1,9 +1,16 @@
 package dev.fitko.fitconnect.api.services.validation;
 
+import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
+import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
+import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -14,28 +21,14 @@ import java.util.Map;
 public interface ValidationService {
 
     /**
-     * Validates the public key consisting of the following steps:
-     * <p>
-     * <ul>
-     *     <li>checks if the JSON Web Key has key operation `wrap_key`</li>
-     *     <li>checks if the JSON Web Key is suitable for the encryption</li>
-     *     <li>checks if the public key is matching the certificate referenced in the JWK</li>
-     *     <li>checks the certificate chain up to the root certificate</li>
-     *     <li>checks against a certificate revocation list and/or an OSCP-endpoint with signed response</li>
-     * </ul>
-     * @param publicKey the public JWK
+     * Validates the public key for integrity.
      *
-     * @return {@link ValidationResult} that includes an error if the validation failed
-     */
-    ValidationResult validateEncryptionPublicKey(RSAKey publicKey);
-
-    /**
-     * Validates the public signature key with key-operation `verify`
+     * @param publicKey the public JWK
+     * @param keyOperation key operation the public key be validated with, represents {@code key_ops} parameter in a JWK
      *
-     * @param signatureKey the public signature JWK
      * @return {@link ValidationResult} that includes an error if the validation failed
      */
-    ValidationResult validateSignaturePublicKey(RSAKey signatureKey);
+    ValidationResult validatePublicKey(final RSAKey publicKey, KeyOperation keyOperation);
 
     /**
      * Validates the metadata against a given schema.
@@ -46,6 +39,38 @@ public interface ValidationService {
      */
     ValidationResult validateMetadataSchema(Metadata metadata);
 
+    /**
+     * Validates the {@link Metadata} structure and contents to ensure its correctness.
+     *
+     * @param metadata the {@link Metadata} object that is validated
+     * @param submission the {@link Submission} of the validated metadata
+     * @param destination the {@link Destination} of the validated metadata
+     * @param authenticationTags the {@link AuthenticationTags} of the validated metadata
+     * @return a {@link ValidationResult}, contains an error if the {@link Metadata} is invalid, e.g. doesn't match the schema
+     */
+    ValidationResult validateMetadata(Metadata metadata, Submission submission, Destination destination, AuthenticationTags authenticationTags);
+
+    /**
+     * Validates the attachment structure and contents to ensure its correctness.
+     *
+     * @param attachmentsForValidation list of attachments containing the hash, decrypted and encrypted data needed for validation
+     * @param authenticationTags the {@link AuthenticationTags} of the validated attachments
+
+     * @return a {@link ValidationResult}, contains an error if the attachment is invalid
+     */
+    ValidationResult validateAttachments(List<AttachmentForValidation> attachmentsForValidation, final AuthenticationTags authenticationTags);
+
+    /**
+     * Validates the {@link Data} structure and contents to ensure its correctness.
+     *
+     * @param decryptedData      the unencrypted data as byte[]
+     * @param submission         the {@link Submission} of the validated data
+     * @param metadata           the {@link Metadata} of the validated data
+     * @param authenticationTags the {@link AuthenticationTags} of the validated data
+     * @return a {@link ValidationResult}, contains an error if the {@link Metadata} is invalid, e.g. doesn't match the schema
+     */
+    ValidationResult validateData(byte[] decryptedData, Submission submission, Metadata metadata, AuthenticationTags authenticationTags);
+
     /**
      * Validates a set event against a given schema.
      *
@@ -101,4 +126,5 @@ public interface ValidationService {
      * @return {@code true} if hmac and timestamp provided by the callback meet the required conditions
      */
     ValidationResult validateCallback(String hmac, Long timestamp, String httpBody, String callbackSecret);
+
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java b/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java
index f91e6e2956ab541239f57752b93247855fcb27f1..c4438300daff840e8dcc9090eb3688004a22651b 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java
@@ -30,7 +30,6 @@ public final class RoutingClient {
      * @throws RoutingException if the signature validation failed
      * @throws RestApiException if a technical error occurred during the query
      */
-
     public List<Route> findDestinations(final DestinationSearch search) throws RoutingException, RestApiException {
         final RouteResult routeResult = routingService.getRoutes(search.getLeikaKey(), search.getArs(), search.getAgs(), search.getAreaId(), search.getOffset(), search.getLimit());
         final ValidationResult result = routeVerifier.validateRouteDestinations(routeResult.getRoutes(), search.getLeikaKey(), search.getArs());
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java b/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
index f33c5c9045b288413700e808b615d3d4720b1db2..dadba3618a93729b84f820d870f0b06ca353e384 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
@@ -1,7 +1,5 @@
 package dev.fitko.fitconnect.client;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
@@ -15,23 +13,17 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.DataIntegrityException;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
 import dev.fitko.fitconnect.client.subscriber.model.DecryptedAttachmentPayload;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
 import dev.fitko.fitconnect.core.util.StopWatch;
+import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -46,14 +38,15 @@ import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKN
 public class SubscriberClient {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(SubscriberClient.class);
-    private static final ObjectMapper MAPPER = new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
+
     private static final int DEFAULT_SUBMISSION_LIMIT = 500;
+
     private final Subscriber subscriber;
-    private final RSAKey privateKey;
+    private final SubmissionReceiver submissionReceiver;
 
-    public SubscriberClient(final Subscriber subscriber, final RSAKey privateKey) {
+    public SubscriberClient(final Subscriber subscriber, final SubmissionReceiver submissionReceiver) {
         this.subscriber = subscriber;
-        this.privateKey = privateKey;
+        this.submissionReceiver = submissionReceiver;
     }
 
     /**
@@ -103,59 +96,14 @@ public class SubscriberClient {
     }
 
     /**
-     * Loads a single {@link SubmissionForPickup} by id.
+     * Loads a single {@link SubmissionForPickup} by id. Auto-rejects invalid submissions where e.g. a validation failed.
      *
-     * @param submissionId unique identifier of a submission
-     * @return {@link ReceivedSubmission} to get the submission {@link Metadata}, {@link Data} and {@link ApiAttachment}s as well as
-     * accept or reject the loaded submission
+     * @param submissionId unique identifier of the requested submission
+     * @return {@link ReceivedSubmission} to get the submission {@link Metadata}, {@link Data} and {@link Attachment}s as well as accept or reject the loaded submission
+     * @throws SubmissionRequestException if a technical error occurred or validation failed
      */
-    public ReceivedSubmission requestSubmission(final UUID submissionId) {
-
-        try {
-
-            final var startTimeDownloadSubmission = StopWatch.start();
-            final Submission submission = subscriber.getSubmission(submissionId);
-            LOGGER.info("Downloading submission took {}", StopWatch.stopWithFormattedTime(startTimeDownloadSubmission));
-
-            LOGGER.info("Decrypting metadata ...");
-            final Metadata metadata = decryptMetadata(submission.getEncryptedMetadata());
-            final ValidationResult metadataValidation = subscriber.validateMetadata(metadata);
-            if (metadataValidation.hasError()) {
-                LOGGER.error("Metadata does not match schema", metadataValidation.getError());
-                return null;
-            }
-
-            LOGGER.info("Decrypting data ...");
-            final byte[] decryptedData = decryptData(submission.getEncryptedData());
-            final String hashFromSender = getDataHashFromMetadata(metadata);
-            final ValidationResult dataValidation = subscriber.validateHashIntegrity(hashFromSender, decryptedData);
-            if (dataValidation.hasError()) {
-                LOGGER.error("Data might be corrupted, hash-sum does not match", dataValidation.getError());
-                return null;
-            }
-
-            LOGGER.info("Loading and decrypting attachments ...");
-            final List<ApiAttachment> attachmentMetadata = metadata.getContentStructure().getAttachments();
-            final List<DecryptedAttachmentPayload> decryptedAttachmentPayloads = loadAttachments(submissionId, attachmentMetadata);
-            final ValidationResult attachmentValidation = validateAttachments(decryptedAttachmentPayloads);
-            if (attachmentValidation.hasError()) {
-                LOGGER.error("One of the attachments is invalid", attachmentValidation.getError());
-                return null;
-            }
-
-            return buildReceivedSubmission(submission, metadata, decryptedData, decryptedAttachmentPayloads);
-
-        } catch (final DecryptionException e) {
-            LOGGER.error("Decrypting payload failed", e);
-        } catch (final RestApiException e) {
-            LOGGER.error("API request failed", e);
-        } catch (final EventCreationException e) {
-            LOGGER.error("Creating SET event failed", e);
-        } catch (final IOException e) {
-            LOGGER.error("Reading metadata failed", e);
-        }
-
-        return null;
+    public ReceivedSubmission requestSubmission(final UUID submissionId) throws SubmissionRequestException {
+        return submissionReceiver.receiveSubmission(submissionId);
     }
 
     /**
@@ -169,83 +117,16 @@ public class SubscriberClient {
         subscriber.rejectSubmission(EventPayload.forRejectEvent(submissionForPickup, rejectionProblems));
     }
 
+    /**
+     * Checks if a received callback can be trusted by validating the provided request data
+     *
+     * @param hmac           authentication code provided by the callback
+     * @param timestamp      timestamp provided by the callback
+     * @param httpBody       HTTP body provided by the callback
+     * @param callbackSecret secret owned by the client, which is used to calculate the hmac
+     * @return {@code true} if hmac and timestamp provided by the callback meet the required conditions
+     */
     public ValidationResult validateCallback(final String hmac, final Long timestamp, final String httpBody, final String callbackSecret) {
         return subscriber.validateCallback(hmac, timestamp, httpBody, callbackSecret);
     }
-
-    private ReceivedSubmission buildReceivedSubmission(final Submission submission, final Metadata metadata, final byte[] decryptedData, final List<DecryptedAttachmentPayload> attachments) {
-        final MimeType mimeType = metadata.getContentStructure().getData().getSubmissionSchema().getMimeType();
-        final ReceivedData receivedData = new ReceivedData(new String(decryptedData, StandardCharsets.UTF_8), mimeType);
-        final List<Attachment> receivedAttachments = attachments.stream().map(this::mapToAttachment).collect(Collectors.toList());
-        // TODO setting auth tags was refactored in #598-auto-reject
-        return new ReceivedSubmission(subscriber, submission, metadata, receivedData, receivedAttachments, Map.of());
-    }
-
-    private Attachment mapToAttachment(final DecryptedAttachmentPayload payload) {
-        final ApiAttachment metadata = payload.getAttachmentMetadata();
-        return Attachment.fromByteArray(payload.getDecryptedContent(), metadata.getMimeType(), metadata.getFilename(), metadata.getDescription());
-    }
-
-    private List<DecryptedAttachmentPayload> loadAttachments(final UUID submissionId, final List<ApiAttachment> attachmentMetadata) {
-        if (attachmentMetadata == null || attachmentMetadata.isEmpty()) {
-            LOGGER.info("Submission contains no attachments");
-            return Collections.emptyList();
-        }
-        final List<DecryptedAttachmentPayload> receivedAttachments = new ArrayList<>();
-        for (final ApiAttachment metadata : attachmentMetadata) {
-            final String encryptedAttachment = downloadAttachment(submissionId, metadata);
-            final byte[] decryptedAttachment = decryptAttachment(metadata, encryptedAttachment);
-            final DecryptedAttachmentPayload decryptedAttachmentPayload = DecryptedAttachmentPayload.builder()
-                    .decryptedContent(decryptedAttachment)
-                    .attachmentMetadata(metadata)
-                    .build();
-            receivedAttachments.add(decryptedAttachmentPayload);
-        }
-        return receivedAttachments;
-    }
-
-    private byte[] decryptAttachment(final ApiAttachment metadata, final String encryptedAttachment) {
-        final var startDecryption = StopWatch.start();
-        final byte[] decryptedAttachment = subscriber.decryptStringContent(privateKey, encryptedAttachment);
-        LOGGER.info("Decrypting attachment {} took {}", metadata.getAttachmentId(), StopWatch.stopWithFormattedTime(startDecryption));
-        return decryptedAttachment;
-    }
-
-    private String downloadAttachment(final UUID submissionId, final ApiAttachment metadata) {
-        final var startDownload = StopWatch.start();
-        final String encryptedAttachment = subscriber.fetchAttachment(submissionId, metadata.getAttachmentId());
-        LOGGER.info("Downloading attachment {} took {}", metadata.getAttachmentId(), StopWatch.stopWithFormattedTime(startDownload));
-        return encryptedAttachment;
-    }
-
-    private byte[] decryptData(final String encryptedData) {
-        return subscriber.decryptStringContent(privateKey, encryptedData);
-    }
-
-    private Metadata decryptMetadata(final String encryptedMetadata) throws IOException {
-        final byte[] metadataBytes = subscriber.decryptStringContent(privateKey, encryptedMetadata);
-        return MAPPER.readValue(metadataBytes, Metadata.class);
-    }
-
-    private ValidationResult validateAttachments(final List<DecryptedAttachmentPayload> decryptedAttachmentPayloads) {
-        for (final DecryptedAttachmentPayload decryptedAttachment : decryptedAttachmentPayloads) {
-            final ApiAttachment attachmentMetadata = decryptedAttachment.getAttachmentMetadata();
-            final UUID attachmentId = attachmentMetadata.getAttachmentId();
-            final ValidationResult result = subscriber.validateHashIntegrity(attachmentMetadata.getHash().getContent(), decryptedAttachment.getDecryptedContent());
-            if (result.hasError()) {
-                LOGGER.error("Attachment data for id {} is corrupted", attachmentId, result.getError());
-                return ValidationResult.error(new DataIntegrityException("Attachment " + attachmentId + " is corrupt"));
-            } else {
-                LOGGER.info("Attachment {} with id {} is valid", attachmentMetadata.getFilename(), attachmentId);
-            }
-        }
-        return ValidationResult.ok();
-    }
-
-    private String getDataHashFromMetadata(final Metadata metadata) {
-        return metadata.getContentStructure()
-                .getData()
-                .getHash()
-                .getContent();
-    }
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java b/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
index 3c5e148f6b30ef550c60135231c9d800d2f14e6d..55af02f9afbe5daa0a203463f756157c3f7fe896 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
@@ -25,6 +25,7 @@ import dev.fitko.fitconnect.api.services.validation.ValidationService;
 import dev.fitko.fitconnect.client.RoutingClient;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.SubscriberClient;
+import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
 import dev.fitko.fitconnect.core.SubmissionSender;
 import dev.fitko.fitconnect.core.SubmissionSubscriber;
 import dev.fitko.fitconnect.core.auth.DefaultOAuthService;
@@ -98,7 +99,9 @@ public final class ClientFactory {
         final String privateKeyPath = readPath(getPrivateDecryptionKeyPathFromSubscriber(subscriberConfig), "Decryption Key");
         final RSAKey privateKey = readRSAKeyFromString(privateKeyPath);
 
-        return new SubscriberClient(subscriber, privateKey);
+        final SubmissionReceiver submissionReceiver = new SubmissionReceiver(subscriber, privateKey, config);
+
+        return new SubscriberClient(subscriber, submissionReceiver);
     }
     /**
      * Create a new {@link RoutingClient} to find destinations and services that is automatically configured via a provided {@link ApplicationConfig}.
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
index adae701b27f894181954fa0cc3a022018d1b80a3..932fe4ae7c3dc4e673997642dc045eeca63cbe91 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
@@ -129,6 +129,15 @@ public class Attachment {
         return new String(data, encoding);
     }
 
+    /**
+     * Get the attachment content as string with a default UTF-8 encoding, e.g. in case the mime-type is json or xml.
+     *
+     * @return utf-8 encoded string of the attachments content.
+     */
+    public String getDataAString() {
+        return new String(data, StandardCharsets.UTF_8);
+    }
+
     /**
      * Filename of the attachment. This filed is optional so it might be null.
      * @return filename as string, null if not present
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..050f5c8d1186ac1759759f789fa2eb6570d2135e
--- /dev/null
+++ b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java
@@ -0,0 +1,252 @@
+package dev.fitko.fitconnect.client.subscriber;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
+import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.AttachmentEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.MissingAttachment;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.MetadataEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.MetadataJsonSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
+import dev.fitko.fitconnect.api.domain.model.event.problems.submission.MissingAuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.submission.NotExactlyOneSubmitEvent;
+import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
+import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
+import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
+import dev.fitko.fitconnect.api.exceptions.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
+import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
+import dev.fitko.fitconnect.api.services.Subscriber;
+import dev.fitko.fitconnect.client.sender.model.Attachment;
+import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
+import dev.fitko.fitconnect.core.util.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
+import static com.nimbusds.jose.jwk.KeyOperation.UNWRAP_KEY;
+
+public class SubmissionReceiver {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SubmissionReceiver.class);
+    private static final ObjectMapper MAPPER = new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+    private final Subscriber subscriber;
+    private final ApplicationConfig config;
+    private final RSAKey privateKey;
+
+    public SubmissionReceiver(final Subscriber subscriber, final RSAKey privateKey, final ApplicationConfig config) {
+        this.subscriber = subscriber;
+        this.privateKey = privateKey;
+        this.config = config;
+    }
+
+    public ReceivedSubmission receiveSubmission(final UUID submissionId) {
+
+        LOGGER.info("Requesting submission ...");
+        final Submission submission = loadSubmission(submissionId);
+
+        LOGGER.info("Loading authentication tags from event log ...");
+        final AuthenticationTags authenticationTags = loadAuthTagsForSubmitEvent(submission);
+
+        LOGGER.info("Decrypting metadata ...");
+        final Metadata metadata = decryptMetadata(submission);
+        validateMetadata(metadata, submission, authenticationTags);
+
+        LOGGER.info("Decrypting data ...");
+        final byte[] decryptedData = decryptData(submission);
+        validateData(submission, metadata, decryptedData, authenticationTags);
+
+        LOGGER.info("Loading and decrypting attachments ...");
+        final List<AttachmentForValidation> attachments = loadAttachments(submission, metadata);
+        validateAttachments(attachments, submission, authenticationTags);
+
+        return buildReceivedSubmission(submission, metadata, decryptedData, attachments);
+    }
+
+    private Submission loadSubmission(final UUID submissionId) {
+        try {
+            final var startTimeDownloadSubmission = StopWatch.start();
+            final Submission submission = subscriber.getSubmission(submissionId);
+            LOGGER.info("Downloading submission took {}", StopWatch.stopWithFormattedTime(startTimeDownloadSubmission));
+            return submission;
+        } catch (final RestApiException e) {
+            throw new SubmissionRequestException(e.getMessage(), e);
+        }
+    }
+
+    private AuthenticationTags loadAuthTagsForSubmitEvent(final Submission submission) {
+        try {
+            return subscriber.getAuthenticationTagsForEvent(Event.SUBMIT, submission);
+        } catch (final EventLogException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#struktur--und-signaturpr%C3%BCfung-der-security-event-tokens
+            final InvalidEventLog problem = new InvalidEventLog();
+            rejectSubmissionWithProblem(submission, problem);
+            throw new SubmissionRequestException(problem.getDetail(), e);
+        } catch (final SubmitEventNotFoundException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#genau-ein-submit-event
+            final NotExactlyOneSubmitEvent problem = new NotExactlyOneSubmitEvent();
+            rejectSubmissionWithProblem(submission, problem);
+            throw new SubmissionRequestException(problem.getDetail());
+        } catch (final AuthenticationTagsEmptyException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#authentication-tags-im-submit-event
+            final MissingAuthenticationTags problem = new MissingAuthenticationTags();
+            rejectSubmissionWithProblem(submission, problem);
+            throw new SubmissionRequestException(problem.getDetail());
+        }
+    }
+
+    private void validateMetadata(final Metadata metadata, final Submission submission, final AuthenticationTags authenticationTags) {
+        final ValidationResult validationResult = subscriber.validateMetadata(metadata, submission, authenticationTags);
+        evaluateValidationResult(submission, validationResult, "Metadata is invalid");
+    }
+
+    private void validateAttachments(final List<AttachmentForValidation> attachmentForValidation, final Submission submission, final AuthenticationTags authenticationTags) {
+        final ValidationResult validationResult = subscriber.validateAttachments(attachmentForValidation, authenticationTags);
+        evaluateValidationResult(submission, validationResult, "Attachment validation failed");
+    }
+
+    private void validateData(final Submission submission, final Metadata metadata, final byte[] decryptedData, final AuthenticationTags authenticationTags) {
+        final ValidationResult validationResult = subscriber.validateData(decryptedData, submission, metadata, authenticationTags);
+        evaluateValidationResult(submission, validationResult, "Data is invalid");
+    }
+
+    private void evaluateValidationResult(final Submission submission, final ValidationResult validationResult, final String errorMessage) throws SubmissionRequestException {
+        if (validationResult.hasProblems()) {
+            rejectSubmissionWithProblem(submission, validationResult.getProblems().toArray(new Problem[0]));
+            throw new SubmissionRequestException(validationResult.hasError() ? validationResult.getError().getMessage() : errorMessage);
+        } else if (validationResult.hasError()) {
+            LOGGER.error(validationResult.getError().getMessage(), validationResult.getError());
+            throw new SubmissionRequestException(validationResult.getError().getMessage(), validationResult.getError());
+        }
+    }
+
+    private ReceivedSubmission buildReceivedSubmission(final Submission submission, final Metadata metadata, final byte[] decryptedData, final List<AttachmentForValidation> attachments) {
+        final MimeType mimeType = metadata.getContentStructure().getData().getSubmissionSchema().getMimeType();
+        final ReceivedData receivedData = new ReceivedData(new String(decryptedData, StandardCharsets.UTF_8), mimeType);
+        final List<Attachment> receivedAttachments = attachments.stream().map(this::toAttachment).collect(Collectors.toList());
+        final Map<UUID, String> encryptedAttachments = attachments.stream().collect(Collectors.toMap(AttachmentForValidation::getAttachmentId, AttachmentForValidation::getEncryptedData));
+        return new ReceivedSubmission(subscriber, submission, metadata, receivedData, receivedAttachments, encryptedAttachments);
+    }
+
+    private Attachment toAttachment(final AttachmentForValidation attachment) {
+        final ApiAttachment metadata = attachment.getAttachmentMetadata();
+        return Attachment.fromByteArray(attachment.getDecryptedData(), metadata.getMimeType(), metadata.getFilename(), metadata.getDescription());
+    }
+
+    private List<AttachmentForValidation> loadAttachments(final Submission submission, final Metadata metadata) {
+        final List<ApiAttachment> attachments = metadata.getContentStructure().getAttachments();
+        if (attachments == null || attachments.isEmpty()) {
+            LOGGER.info("Submission contains no attachments");
+            return Collections.emptyList();
+        }
+        final List<AttachmentForValidation> receivedAttachments = new ArrayList<>();
+        for (final ApiAttachment attachmentMetadata : attachments) {
+            final String encryptedAttachment = downloadAttachment(submission, attachmentMetadata);
+            final byte[] decryptedAttachment = decryptAttachment(attachmentMetadata, encryptedAttachment, submission);
+            receivedAttachments.add(new AttachmentForValidation(attachmentMetadata, encryptedAttachment, decryptedAttachment));
+        }
+        return receivedAttachments;
+    }
+
+    private byte[] decryptAttachment(final ApiAttachment metadata, final String encryptedAttachment, final Submission submission) {
+        checkPrivateKey();
+        try {
+            final var startDecryption = StopWatch.start();
+            final byte[] decryptedAttachment = subscriber.decryptStringContent(privateKey, encryptedAttachment);
+            LOGGER.info("Decrypting attachment {} took {}", metadata.getAttachmentId(), StopWatch.stopWithFormattedTime(startDecryption));
+            return decryptedAttachment;
+        } catch (final DecryptionException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#entschl%C3%BCsselung-2
+            rejectSubmissionWithProblem(submission, new AttachmentEncryptionIssue(metadata.getAttachmentId()));
+            throw new SubmissionRequestException(e.getMessage(), e);
+        }
+    }
+
+    private String downloadAttachment(final Submission submission, final ApiAttachment metadata) {
+        try {
+            final var startDownload = StopWatch.start();
+            final String encryptedAttachment = subscriber.fetchAttachment(submission.getSubmissionId(), metadata.getAttachmentId());
+            LOGGER.info("Downloading attachment {} took {}", metadata.getAttachmentId(), StopWatch.stopWithFormattedTime(startDownload));
+            return encryptedAttachment;
+        } catch (final RestApiException e) {
+            if (e.getHttpStatus() != null && e.getHttpStatus().equals(HttpStatus.NOT_FOUND)) {
+                rejectSubmissionWithProblem(submission, new MissingAttachment(metadata.getAttachmentId()));
+            }
+            throw new SubmissionRequestException(e.getMessage(), e);
+        }
+    }
+
+    private byte[] decryptData(final Submission submission) {
+        checkPrivateKey();
+        try {
+            return subscriber.decryptStringContent(privateKey, submission.getEncryptedData());
+        } catch (final DecryptionException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#entschl%C3%BCsselung-1
+            rejectSubmissionWithProblem(submission, new DataEncryptionIssue());
+            throw new SubmissionRequestException(e.getMessage(), e);
+        }
+    }
+
+    private Metadata decryptMetadata(final Submission submission) {
+        checkPrivateKey();
+        try {
+            final byte[] metadataBytes = subscriber.decryptStringContent(privateKey, submission.getEncryptedMetadata());
+            return MAPPER.readValue(metadataBytes, Metadata.class);
+        } catch (final IOException e) {
+            rejectSubmissionWithProblem(submission, new MetadataJsonSyntaxViolation());
+            throw new SubmissionRequestException(e.getMessage(), e);
+        } catch (final DecryptionException e) {
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#entschl%C3%BCsselung
+            rejectSubmissionWithProblem(submission, new MetadataEncryptionIssue());
+            throw new SubmissionRequestException(e.getMessage(), e);
+        }
+    }
+
+    private void checkPrivateKey() {
+        if (!isUnwrapKey(privateKey) && !isEncryptionKeyUse(privateKey)) {
+            throw new SubmissionRequestException("Private key is not suitable for decryption, could not find key_operation unwrapKey or key_use encryption");
+        }
+    }
+
+    private static boolean isEncryptionKeyUse(final RSAKey privateKey) {
+        return privateKey.getKeyUse() != null && privateKey.getKeyUse().equals(KeyUse.ENCRYPTION);
+    }
+
+    private static boolean isUnwrapKey(final RSAKey privateKey) {
+        return privateKey.getKeyOperations() != null && privateKey.getKeyOperations().stream()
+                .anyMatch(keyOp -> keyOp.identifier().equals(UNWRAP_KEY.identifier()));
+    }
+
+    private void rejectSubmissionWithProblem(final Submission submission, final Problem... problem) {
+        reject(submission, List.of(problem));
+    }
+
+    private void reject(final Submission submission, final List<Problem> problems) {
+        if (config.isEnableAutoReject()) {
+            subscriber.rejectSubmission(EventPayload.forRejectEvent(submission, problems));
+        }
+    }
+}
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/ClientIntegrationTest.java b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/model/ReceivedAttachment.java
similarity index 100%
rename from client/src/test/java/dev/fitko/fitconnect/client/ClientIntegrationTest.java
rename to client/src/main/java/dev/fitko/fitconnect/client/subscriber/model/ReceivedAttachment.java
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
index 118fbc8d6607723cd411002cbbf3a3af23ac87fe..bd084d15eea87238488c2a222a495efb700bbc45 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
@@ -102,7 +102,6 @@ public class SenderClientTest {
         when(senderMock.getDestination(any())).thenReturn(destination);
         when(senderMock.createSubmission(any())).thenReturn(announcedSubmission);
         when(senderMock.sendSubmission(any())).thenReturn(expectedSubmission);
-        when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
         when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
         when(senderMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
@@ -144,7 +143,6 @@ public class SenderClientTest {
         when(senderMock.getDestination(any())).thenReturn(destination);
         when(senderMock.createSubmission(any())).thenReturn(announcedSubmission);
         when(senderMock.sendSubmission(any())).thenReturn(expectedSubmission);
-        when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
         when(senderMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
         when(senderMock.validateXmlFormat(any())).thenReturn(ValidationResult.ok());
@@ -184,7 +182,6 @@ public class SenderClientTest {
 
         when(senderMock.getDestination(any())).thenReturn(destination);
         when(senderMock.createSubmission(any())).thenReturn(announcedSubmission);
-        when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.validateJsonFormat(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
 
@@ -379,7 +376,6 @@ public class SenderClientTest {
         final var destination = getDestination(destinationId);
 
         when(senderMock.getDestination(any())).thenReturn(destination);
-        when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
 
         // When
@@ -509,7 +505,6 @@ public class SenderClientTest {
 
         when(senderMock.getDestination(any())).thenReturn(destination);
         when(senderMock.createSubmission(any())).thenReturn(announcedSubmission);
-        when(senderMock.validatePublicKey(any())).thenReturn(ValidationResult.ok());
         when(senderMock.getEncryptionKeyForDestination(any())).thenReturn(publicKey);
         when(senderMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
         when(senderMock.validateXmlFormat(any())).thenReturn(ValidationResult.ok());
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
index 5aceb12ff04ee62b80a7cbb5bba57196ae4a649e..00e535a6f06020080d7a0b2f6111e8416d7a5166 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
@@ -3,9 +3,20 @@ package dev.fitko.fitconnect.client;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.nimbusds.jose.jwk.RSAKey;
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.AttachmentEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.IncorrectAttachmentAuthenticationTag;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.MissingAttachment;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.IncorrectMetadataAuthenticationTag;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
+import dev.fitko.fitconnect.api.domain.model.event.problems.submission.MissingAuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.submission.NotExactlyOneSubmitEvent;
 import dev.fitko.fitconnect.api.domain.model.metadata.ContentStructure;
 import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
@@ -17,22 +28,27 @@ import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
 import dev.fitko.fitconnect.api.exceptions.DecryptionException;
 import dev.fitko.fitconnect.api.exceptions.EventLogException;
 import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
+import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
 import dev.fitko.fitconnect.api.exceptions.ValidationException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
-import dev.fitko.fitconnect.client.testutil.LogCaptor;
+import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
 import dev.fitko.fitconnect.core.crypto.HashService;
 import dev.fitko.fitconnect.core.crypto.JWECryptoService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
+import org.springframework.http.HttpStatus;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.text.ParseException;
 import java.util.Collections;
@@ -47,10 +63,10 @@ import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.times;
@@ -60,7 +76,6 @@ import static org.mockito.Mockito.when;
 class SubscriberClientTest {
 
     private static final ObjectMapper mapper = new ObjectMapper();
-    private final LogCaptor logs = new LogCaptor();
 
     private Subscriber subscriberMock;
     private RSAKey privateKey;
@@ -68,9 +83,10 @@ class SubscriberClientTest {
 
     @BeforeEach
     public void setup() throws IOException, ParseException {
+        final ApplicationConfig config = new ApplicationConfig();
         privateKey = RSAKey.parse(getResourceAsString("private_decryption_test_key.json"));
         subscriberMock = Mockito.mock(Subscriber.class);
-        underTest = new SubscriberClient(subscriberMock, privateKey);
+        underTest = new SubscriberClient(subscriberMock, new SubmissionReceiver(subscriberMock, privateKey, config));
     }
 
     @Test
@@ -79,7 +95,7 @@ class SubscriberClientTest {
         assertThat(underTest.getAvailableSubmissionsForDestination(UUID.randomUUID()).isEmpty(), is(true));
 
         final var destinationId = UUID.randomUUID();
-        when(subscriberMock.pollAvailableSubmissionsForDestination(destinationId, 0, 5)).thenReturn(Collections.EMPTY_SET);
+        when(subscriberMock.pollAvailableSubmissionsForDestination(destinationId, 0, 5)).thenReturn(Collections.emptySet());
         assertThat(underTest.getAvailableSubmissionsForDestination(destinationId).isEmpty(), is(true));
     }
 
@@ -89,31 +105,35 @@ class SubscriberClientTest {
         when(subscriberMock.getSubmission(any())).thenThrow(new RestApiException("Submission not found"));
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(UUID.randomUUID());
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("API request failed");
+        assertThat(exception.getMessage(), containsString("Submission not found"));
     }
 
     @Test
     void testDecryptionFailed() {
         // Given
         final var submissionId = UUID.randomUUID();
+
         final var submission = new Submission();
         submission.setSubmissionId(submissionId);
         submission.setEncryptedData("encryptedData");
         submission.setEncryptedMetadata("encryptedMetadata");
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTags");
+        authenticationTags.setData("dataAuthTags");
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenReturn(authenticationTags);
         when(subscriberMock.decryptStringContent(any(), any())).thenThrow(new DecryptionException("Decryption failed"));
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(submissionId);
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("Decrypting payload failed");
+        assertThat(exception.getMessage(), containsString("Decryption failed"));
     }
 
     @Test
@@ -125,6 +145,7 @@ class SubscriberClientTest {
         submission.setSubmissionId(submissionId);
         submission.setCaseId(UUID.randomUUID());
         submission.setDestinationId(UUID.randomUUID());
+        submission.setEncryptedMetadata("abc");
 
         final var hash = new Hash();
         hash.setContent("hashedTestContent");
@@ -143,10 +164,18 @@ class SubscriberClientTest {
         final var metadata = new Metadata();
         metadata.setContentStructure(contentStructure);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("abc");
+        authenticationTags.setData("def");
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn(mapper.writeValueAsBytes(metadata));
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.validateAttachments(any(), any())).thenReturn(ValidationResult.ok());
+
 
         // When
         underTest.requestSubmission(submissionId).acceptSubmission();
@@ -164,6 +193,7 @@ class SubscriberClientTest {
         submission.setSubmissionId(submissionId);
         submission.setCaseId(UUID.randomUUID());
         submission.setDestinationId(UUID.randomUUID());
+        submission.setEncryptedMetadata("abc");
 
         final var hash = new Hash();
         hash.setContent("hashedTestContent");
@@ -181,16 +211,24 @@ class SubscriberClientTest {
         final var metadata = new Metadata();
         metadata.setContentStructure(contentStructure);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("abc");
+        authenticationTags.setData("def");
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn(mapper.writeValueAsBytes(metadata));
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.validateAttachments(any(), any())).thenReturn(ValidationResult.ok());
+
 
         // When
-        underTest.requestSubmission(submissionId).rejectSubmission(List.of(new InvalidEventLog()));
+        underTest.requestSubmission(submissionId).rejectSubmission(List.of(new DataEncryptionIssue()));
 
         // Then
-        final var expectedEventPayload = EventPayload.forRejectEvent(submission, List.of(new InvalidEventLog()));
+        final var expectedEventPayload = EventPayload.forRejectEvent(submission, List.of(new DataEncryptionIssue()));
         verify(subscriberMock, times(1)).rejectSubmission(expectedEventPayload);
     }
 
@@ -198,20 +236,25 @@ class SubscriberClientTest {
     void testReadingMetadataFailed() {
         // Given
         final var submissionId = UUID.randomUUID();
+
         final var submission = new Submission();
         submission.setSubmissionId(submissionId);
         submission.setEncryptedData("encryptedData");
         submission.setEncryptedMetadata("encryptedMetadata");
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTags");
+        authenticationTags.setData("dataAuthTags");
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenReturn(authenticationTags);
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn("encryptedMetadata".getBytes());
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(submissionId);
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("Reading metadata failed");
+        assertThat(exception.getMessage(), containsString("Unrecognized token 'encryptedMetadata'"));
     }
 
     @Test
@@ -273,11 +316,18 @@ class SubscriberClientTest {
         submission.setEncryptedData(encryptedData);
         submission.setEncryptedMetadata(encryptedMetadata);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setData(encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateAttachments(any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedData())).thenReturn(dataPayload.getBytes());
         when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedMetadata())).thenReturn(metadataBytes);
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
 
         // When
         final var receivedSubmission = underTest.requestSubmission(submissionId);
@@ -295,14 +345,14 @@ class SubscriberClientTest {
         when(subscriberMock.getSubmission(any())).thenThrow(new RestApiException("Submission not found"));
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(UUID.randomUUID());
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
 
         // Then
-        assertNull(receivedSubmission);
+        assertThat(exception.getMessage(), containsString("Submission not found"));
     }
 
     @Test
-    void testInvalidMetadata() throws JsonProcessingException {
+    void testMetadataValidationFailed() throws JsonProcessingException {
         // Given
         final var submissionId = UUID.randomUUID();
         final var destinationId = UUID.randomUUID();
@@ -312,19 +362,27 @@ class SubscriberClientTest {
         submission.setSubmissionId(submissionId);
         submission.setDestinationId(destinationId);
         submission.setCaseId(caseId);
+        submission.setEncryptedMetadata("abc");
+
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("abc");
 
         final var invalidMetadata = mapper.writeValueAsBytes(new Metadata());
 
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn(invalidMetadata);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.error(new ValidationException("Validation failed")));
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.error(new ValidationException("Metadata does not match schema")));
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+
+        final IncorrectMetadataAuthenticationTag problem = new IncorrectMetadataAuthenticationTag();
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.problem(problem));
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(submissionId);
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("Metadata does not match schema");
+        assertThat(exception.getMessage(), containsString("Metadata is invalid"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
     }
 
     @Test
@@ -338,6 +396,7 @@ class SubscriberClientTest {
         submission.setSubmissionId(submissionId);
         submission.setDestinationId(destinationId);
         submission.setCaseId(caseId);
+        submission.setEncryptedMetadata("abc");
 
         final Map<String, Map<?, ?>> metadata = Map.of(
                 "$schemaFoo", Collections.emptyMap(),
@@ -345,7 +404,7 @@ class SubscriberClientTest {
                         "data", Map.of(
                                 "hash", Map.of(
                                         "type", SignatureType.SHA_512,
-                                        "content", "bla"
+                                        "content", "foo"
                                 ),
                                 "submissionSchema", Map.of(
                                         "schemaUri", URI.create("https://dummy.schema.url"),
@@ -357,17 +416,24 @@ class SubscriberClientTest {
 
         final var invalidMetadata = mapper.writeValueAsBytes(metadata);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("abc");
+        authenticationTags.setData("def");
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn(invalidMetadata);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.validateAttachments(any(), any())).thenReturn(ValidationResult.ok());
+
 
         // When
         final var receivedSubmission = underTest.requestSubmission(submissionId);
 
         // Then
-        assertNotNull(receivedSubmission);
-        assertThat(receivedSubmission.getMetadata().getContentStructure().getData().getHash().getContent(), is("bla"));
+        assertThat(receivedSubmission.getMetadata().getContentStructure().getData().getHash().getContent(), is("foo"));
     }
 
     @Test
@@ -408,11 +474,18 @@ class SubscriberClientTest {
         submission.setEncryptedData(encryptedData);
         submission.setEncryptedMetadata(encryptedMetadata);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setData(encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
         when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedData())).thenReturn(dataPayload.getBytes());
         when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedMetadata())).thenReturn(metadataBytes);
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.validateAttachments(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final var receivedSubmission = underTest.requestSubmission(submissionId);
@@ -423,7 +496,7 @@ class SubscriberClientTest {
     }
 
     @Test
-    void testCorruptedData() throws JsonProcessingException {
+    void testDataValidationFailed() throws JsonProcessingException {
 
         // Given
         final RSAKey decryptionKey = privateKey;
@@ -457,22 +530,32 @@ class SubscriberClientTest {
         submission.setEncryptedMetadata(encryptedMetadata);
         submission.setEncryptedData(encryptedData);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setData(encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(decryptionKey, encryptedMetadata)).thenReturn(metadataBytes);
         when(subscriberMock.decryptStringContent(decryptionKey, encryptedData)).thenReturn(dataBytes);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
-        when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.error(new ValidationException("Corrupt data hash")));
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+
+        final DataHashMismatch problem = new DataHashMismatch();
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.problem(problem));
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(submissionId);
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("Data might be corrupted, hash-sum does not match");
+        assertThat(exception.getMessage(), containsString("Data is invalid"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
+
     }
 
     @Test
-    void testCorruptedAttachment() throws JsonProcessingException {
+    void testAttachmentValidationFailed() throws JsonProcessingException {
         // Given
         final RSAKey decryptionKey = privateKey;
         final RSAKey encryptionKey = decryptionKey.toPublicJWK();
@@ -486,6 +569,7 @@ class SubscriberClientTest {
         final var attachmentHash = new Hash();
         attachmentHash.setContent("");
         final var attachment = new ApiAttachment();
+        attachment.setAttachmentId(UUID.randomUUID());
         attachment.setFilename("src/test/resources/attachment.txt");
         attachment.setHash(attachmentHash);
 
@@ -511,21 +595,154 @@ class SubscriberClientTest {
         submission.setEncryptedMetadata(encryptedMetadata);
         submission.setEncryptedData(encryptedData);
 
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setData(encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
         when(subscriberMock.getSubmission(any())).thenReturn(submission);
         when(subscriberMock.decryptStringContent(decryptionKey, encryptedMetadata)).thenReturn(metadataBytes);
         when(subscriberMock.decryptStringContent(decryptionKey, encryptedData)).thenReturn(dataBytes);
-        when(subscriberMock.validateMetadata(any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
 
-        final var decryptedDataBytes = cryptoService.decryptToBytes(decryptionKey, submission.getEncryptedData());
-        when(subscriberMock.validateHashIntegrity(dataHash.getContent(), decryptedDataBytes)).thenReturn(ValidationResult.ok());
-        when(subscriberMock.validateHashIntegrity(attachmentHash.getContent(), null)).thenReturn(ValidationResult.error(new ValidationException("Corrupt attachment")));
+        final IncorrectAttachmentAuthenticationTag problem = new IncorrectAttachmentAuthenticationTag(attachment.getAttachmentId());
+        when(subscriberMock.validateAttachments(anyList(), any())).thenReturn(ValidationResult.problem(problem));
 
         // When
-        final var receivedSubmission = underTest.requestSubmission(submissionId);
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+
+        // Then
+        assertThat(exception.getMessage(), containsString("Attachment validation failed"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
+    }
+
+    @Test
+    void testMissingAttachment() throws Exception {
+
+        // Given
+        final var dataPayload = "{ some : 'data foo' }";
+        final var submissionId = UUID.randomUUID();
+        final CryptoService cryptoService = new JWECryptoService(new HashService());
+
+        final Hash hash = new Hash();
+        hash.setContent(cryptoService.hashBytes(dataPayload.getBytes()));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final SubmissionSchema schema = new SubmissionSchema();
+        schema.setSchemaUri(URI.create("https://dummy.schema.url"));
+        schema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(schema);
+        data.setHash(hash);
+
+        final ApiAttachment attachment = new ApiAttachment();
+        attachment.setAttachmentId(UUID.randomUUID());
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setAttachments(List.of(attachment));
+        contentStructure.setData(data);
+
+        final Metadata metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final byte[] metadataBytes = mapper.writeValueAsBytes(metadata);
+
+        final RSAKey encryptionKey = privateKey;
+        final RSAKey decryptionKey = encryptionKey.toPublicJWK();
+
+        final String encryptedData = cryptoService.encryptBytes(decryptionKey, dataPayload.getBytes(StandardCharsets.UTF_8));
+        final String encryptedMetadata = cryptoService.encryptBytes(decryptionKey, metadataBytes);
+
+        final var submission = new Submission();
+        submission.setSubmissionId(submissionId);
+        submission.setEncryptedData(encryptedData);
+        submission.setEncryptedMetadata(encryptedMetadata);
+
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
+        when(subscriberMock.getSubmission(any())).thenReturn(submission);
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedData())).thenReturn(dataPayload.getBytes());
+        when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedMetadata())).thenReturn(metadataBytes);
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.fetchAttachment(any(), any())).thenThrow(new RestApiException(HttpStatus.NOT_FOUND, "Attachment download failed"));
+
+        // When
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+
+        // Then
+        assertThat(exception.getMessage(), containsString("Attachment download failed"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(new MissingAttachment(attachment.getAttachmentId()))));
+    }
+
+    @Test
+    void testAttachmentEncryptionIssue() throws Exception {
+
+        // Given
+        final var dataPayload = "{ some : 'data foo' }";
+        final var submissionId = UUID.randomUUID();
+        final CryptoService cryptoService = new JWECryptoService(new HashService());
+
+        final Hash hash = new Hash();
+        hash.setContent(cryptoService.hashBytes(dataPayload.getBytes()));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final SubmissionSchema schema = new SubmissionSchema();
+        schema.setSchemaUri(URI.create("https://dummy.schema.url"));
+        schema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(schema);
+        data.setHash(hash);
+
+        final ApiAttachment attachment = new ApiAttachment();
+        attachment.setAttachmentId(UUID.randomUUID());
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setAttachments(List.of(attachment));
+        contentStructure.setData(data);
+
+        final Metadata metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final byte[] metadataBytes = mapper.writeValueAsBytes(metadata);
+
+        final RSAKey encryptionKey = privateKey;
+        final RSAKey decryptionKey = encryptionKey.toPublicJWK();
+
+        final String encryptedData = cryptoService.encryptBytes(decryptionKey, dataPayload.getBytes(StandardCharsets.UTF_8));
+        final String encryptedMetadata = cryptoService.encryptBytes(decryptionKey, metadataBytes);
+
+        final var submission = new Submission();
+        submission.setSubmissionId(submissionId);
+        submission.setEncryptedData(encryptedData);
+        submission.setEncryptedMetadata(encryptedMetadata);
+
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata(encryptedMetadata.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN)[4]);
+
+        when(subscriberMock.getSubmission(any())).thenReturn(submission);
+        when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.validateHashIntegrity(any(), any())).thenReturn(ValidationResult.ok());
+        when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedData())).thenReturn(dataPayload.getBytes());
+        when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedMetadata())).thenReturn(metadataBytes);
+        when(subscriberMock.decryptStringContent(encryptionKey, "encrypt$dAtt@chm$nt")).thenThrow(new DecryptionException("Decrypting attachment failed"));
+        when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
+        when(subscriberMock.fetchAttachment(any(), any())).thenReturn("encrypt$dAtt@chm$nt");
+
+        // When
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertNull(receivedSubmission);
-        logs.assertContains("Attachment data for id null is corrupted");
+        assertThat(exception.getMessage(), containsString("Decrypting attachment failed"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(new AttachmentEncryptionIssue(attachment.getAttachmentId()))));
     }
 
     @Test
@@ -547,20 +764,63 @@ class SubscriberClientTest {
     }
 
     @Test
-    void testGetInvalidEventLog() {
+    void testNotExactlyOneSubmitEvent() {
 
         // Given
-        final var destinationId = UUID.randomUUID();
-        final var caseId = UUID.randomUUID();
+        final var submission = new Submission();
+        submission.setSubmissionId(UUID.randomUUID());
+
+        final var expectedProblem = new NotExactlyOneSubmitEvent();
+
+        when(subscriberMock.getSubmission(submission.getSubmissionId())).thenReturn(submission);
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new SubmitEventNotFoundException("no submit event in log"));
+
+        // When
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
+
+        // Then
+        assertThat(exception.getMessage(), containsString("The Event-Log is inconsistent because it does not contain exactly one 'submit' event."));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission,List.of(expectedProblem)));
+    }
 
-        when(subscriberMock.getEventLog(any(), any())).thenThrow(new EventLogException("Invalid log"));
+    @Test
+    void testMissingAuthenticationTags() {
+
+        // Given
+        final var submission = new Submission();
+        submission.setSubmissionId(UUID.randomUUID());
+
+        final var expectedProblem = new MissingAuthenticationTags();
+
+        when(subscriberMock.getSubmission(submission.getSubmissionId())).thenReturn(submission);
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new AuthenticationTagsEmptyException("empty auth tags"));
 
         // When
-        final EventLogException exception = assertThrows(EventLogException.class, () -> underTest.getEventLog(caseId, destinationId));
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Invalid log"));
+        assertThat(exception.getMessage(), containsString("The 'submit-submission' event does not contain authentication tags"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission,List.of(expectedProblem)));
+    }
+
+    @Test
+    void testInvalidEventLog() {
 
+        // Given
+        final var submission = new Submission();
+        submission.setSubmissionId(UUID.randomUUID());
+
+        final InvalidEventLog expectedProblem = new InvalidEventLog();
+
+        when(subscriberMock.getSubmission(submission.getSubmissionId())).thenReturn(submission);
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new EventLogException("Invalid log"));
+
+        // When
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
+
+        // Then
+        assertThat(exception.getMessage(), containsString("The Event-Log is inconsistent"));
+        verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission,List.of(expectedProblem)));
     }
 
     @Test
diff --git a/client/src/test/resources/private_decryption_test_key.json b/client/src/test/resources/private_decryption_test_key.json
index 9be02d7261d9283ac546d8e3eaee54deb84693e9..c5c076ebf299198b47a5f91e457821d52ef36c88 100644
--- a/client/src/test/resources/private_decryption_test_key.json
+++ b/client/src/test/resources/private_decryption_test_key.json
@@ -1 +1,16 @@
-{"alg":"RSA-OAEP-256","d":"UuZ7DtiI46pxCeACoP6cDxWBT2PJea6Td5eTU2sm-jkGH1O_ShgBtqHI6KwOLenO0Z1GlHoVNzIx9NeazUpVdN1Oan42ximA6LXqP6GaaWD7qJmzuSER968F4StW7hv5aI1N35mFLESY2ENISfhmRGBsCalRrQVFU5lRwjH22m_JTdeMeRbCCZHxynH2GcOwei8lZgWTiu3cIz7giEwc3GrZHZMxhdBLBCJ3gxDer9O_V0UCr7W7VpLpFuaOZmKaB2io-Rgz5W71IIo-09IghWnV5VZWpXr1Dl1_UbhE9UK_N6gQ9HcFopBQR6huD2svUO2aVig3RLO1KG8AL6vQsp8uIa_7_TuVYnuo7S_zVRlungjs2UzABjRqsdUED9C-FmwpnbKcHA1V99U8Ag22VHXm0zcT58M09-_yUl9s4VG6y-yQEhfxTjL0LMtSL8XScRwf38mpgX54LFGOeImo8d6jFGbNMzMViStejmXU8nCBeRmEtHpDU-GxJSPhO0ZJNgNWH_RU-tmujodXxXvyp7yA3MFqvcKxUQGf73lz_y10lLSmAJSlG-3KYyHY1N5AlXF4m0N3yENcim3VPVZarpOgHfEF3gg3slthAFZqTeY2WKMBp2O_QBvyf3mylVKHECzY4XzScBiPA9qlbeRDoa-NxcQwT2bS_1cyk8_IYG8","dp":"-sRGGYORpYDRglqvK9RX6FCQUpjCwbyAeq3yU4sEnC2XWt37-bw-p0KzSs7OuTTSPNH5I9HrfAzVHPzGXWE_CJmkdvkGqLkmvFTFiuGY1yk3psYvLwzDPJjyKUzdI-GMdioL_IyjEfna2TxdiYLyzGCDtGJxrSVyunc2xcMx_k1u_HQcylog00P61kCnUVtGsDqMbYsShCQaZXwQK3wPL6KkjgVLtEYYYeP8NiJRL_XP8SCD4ebU_cJMLDWgq1DgxpfqIBz0fCNe0ty9LwEhMhmP6OpmU5mRAGzXNKOp_lYYEVmvRoEwcRyFdM1W4N61eu8EYGyfzTrH1gfG8FMLfQ","dq":"uC9tAhQI7vRE2isCeoxY3YgJd2RBOdRrIjowUoS7WuEho_aLKWgoJDSzZoXad9pindjaSRj9CRKZ7oRW1Dp0NQwTQdUsyZSZt4cB5yxg50ty5IE81XDX0pgyRCJOXNpKxSZYtcBUzqlId4dCRd2codqaKJkP77K0TmWlQve_CoCPuaSP4CGqt8GpQj1fKzI_NYYqNVk-1jSAa9PZ2E-gW0iJwQoUNkDRTBgIVLx1qu7aYieYeif8oTFjKGKkP3EXCTGXKSdLd729-37NgWR0YktLFQsWfiNHcy1_-eWI6HKc8onOv5IuchKQ9nmQIE1mQUZBATCtx-vdzQqF_alTKw","e":"AQAB","key_ops":["unwrapKey"],"kid":"jsMQHFiA2uMGtiG3Ro8RJnYdEhp5W5KjGW-Vcf3-YMk","kty":"RSA","n":"zEi7NpGkzGyG2-PDWTy72hvti-pGBZLidxbk10_fenjzOavKcO5yGXSCfX_Xl0-WYaJfb7Kz6CRRtnwBGx8mrsftodtxt3kdrFnf6chQ2JJ5dmnz_ErIbHjHaFlxXvEqv3ivqIQSZvuns8QJip8RGQ63g7nmPDyvBcvLMFUtnXy7Y6rzUI4HO2eeg8htMPCWN5-L7Ol5_IT11NuZK6J3_UN1B0P7fFGdVMhsNR5D_jHOD-U4jZQijyVgzIXxwN_vnf90e5_ZUgsLypwh2DT7qzhqES8hzIIk_Cjs5mCUwDjfiBh0g7LNjXaj0b7rAn2X6yPuMK6zYi_FXJYSCQ7LH3THi_h5r2--xQ2h40He23JjfNpEGu98uMgB_dvzH0Dco8OKh4Aj2wjLpZVFOS3Zpu-WUalP-ecEiZ6nzmCMoIpWM0U4t4tpGwC1X-MWNj75zYLI8-yd3ELVBx3PQSzDukGOVLBdtpF0txf7Np0i9RFRW-nxf3xPVFCbZYbc87GMZy1CeDT2NBJJHtQaPKBismnEXtXtiM7aJDRM74F5JXjDVR62eHGxFCy46oyI_NS3FmvkoVtV8hvbXBRMSU8sNeOrVKnbWpfjBGFGD8VbssKoAAdMfn6usOiiH71SBg-L9KoBwdRfu7XFE-WbSA2d03hMvEPJWk9srh5qGdtQ_QE","p":"_RRumCEIUoM1gipAOIeQZ_td0E2qx20bFHArBf1V7ev4v4S22tt1Bt9oa20KPfA6mokycaB8MBCX7LTRvBT1XwSh4sU2TptuibElrarQvFoJIVGqugqG4GxsBpZpy-Dvx9NJtMTjiiGXSn87-_8iSWrPlc4Vdbgb8S9Vz2x_Mp-uSwZaFOO3Gx4miD29y1ugE26hFF0RdgBDJnJEk71W9NG2D_MbnBBa3DrPesdU8rX08RurpV8C1TVUgelDxUBQUxrRXRaHOPbrwPBBRDOgzNlFiuzUdkw6qwlfhp9W1tn3j7uAnqMHxnaPhJJ51tFjd_7MgSawmmM415c4NWa5xw","q":"zqQpmsvtjeU486C2DZ_NjsDHanYXgfxiY3X25XpM0ojLrtIQWKUaFxlggJGSQzQ6QJqB-5sJu9EEE4DGZ3UYoakY_hzxv0uHOHrI20ohFgF5K3YQdqGRhC3Bo66ezpmD3FT1EPXQM2OLEAi82lUb7UrbP_DSU0BmYOd6kPd1a3FD2qm0DjA15IYW5vUHNisx79W3kt_8s7ZICjiVpvJTSa1TVIX3wPM0qLHOWbHEOswLENC4fZ7EFYkNr3MvtAOxLcJ_mi4zzhAkyGdKJbTQXt6bhD_gWL5K1iOFM5Vg_NRTjUMqndDK33KDYZhC-MexSR-IaN7MbP1Y787uFL5S9w","qi":"DHzvGmAC4OZIfvDBkqXrvrvBeqURfioiEN72SL6oPcBqof899UL50BVohU0a2IeydwLSSff1i0U0jfX8ss_YKnCw2MxH3eNdQ-JXAQp7sefYLiFKPlGQnaxCNL0Z3kDatPhJnfTHaJA_UKtuPtcIINK5lJH80Iqqg3rl1p5G84ujbIghcgJXGoRGjJfkpUQgarTwPzdScR8Xo-YmN4dNav2X76s5toTJbSc4wLh3z_vdgL4GTGv9jzRKBPJcv07SDOFz92VA2r2DiIPQxg1eAKq8MPwEQ_bkaqh6Sk57_-JT8CRN0vfZbHw_3jCc8ijqRQE-4HaKcVCAbgnTzgr8cw"}
\ No newline at end of file
+{
+  "alg": "RSA-OAEP-256",
+  "d": "UuZ7DtiI46pxCeACoP6cDxWBT2PJea6Td5eTU2sm-jkGH1O_ShgBtqHI6KwOLenO0Z1GlHoVNzIx9NeazUpVdN1Oan42ximA6LXqP6GaaWD7qJmzuSER968F4StW7hv5aI1N35mFLESY2ENISfhmRGBsCalRrQVFU5lRwjH22m_JTdeMeRbCCZHxynH2GcOwei8lZgWTiu3cIz7giEwc3GrZHZMxhdBLBCJ3gxDer9O_V0UCr7W7VpLpFuaOZmKaB2io-Rgz5W71IIo-09IghWnV5VZWpXr1Dl1_UbhE9UK_N6gQ9HcFopBQR6huD2svUO2aVig3RLO1KG8AL6vQsp8uIa_7_TuVYnuo7S_zVRlungjs2UzABjRqsdUED9C-FmwpnbKcHA1V99U8Ag22VHXm0zcT58M09-_yUl9s4VG6y-yQEhfxTjL0LMtSL8XScRwf38mpgX54LFGOeImo8d6jFGbNMzMViStejmXU8nCBeRmEtHpDU-GxJSPhO0ZJNgNWH_RU-tmujodXxXvyp7yA3MFqvcKxUQGf73lz_y10lLSmAJSlG-3KYyHY1N5AlXF4m0N3yENcim3VPVZarpOgHfEF3gg3slthAFZqTeY2WKMBp2O_QBvyf3mylVKHECzY4XzScBiPA9qlbeRDoa-NxcQwT2bS_1cyk8_IYG8",
+  "dp": "-sRGGYORpYDRglqvK9RX6FCQUpjCwbyAeq3yU4sEnC2XWt37-bw-p0KzSs7OuTTSPNH5I9HrfAzVHPzGXWE_CJmkdvkGqLkmvFTFiuGY1yk3psYvLwzDPJjyKUzdI-GMdioL_IyjEfna2TxdiYLyzGCDtGJxrSVyunc2xcMx_k1u_HQcylog00P61kCnUVtGsDqMbYsShCQaZXwQK3wPL6KkjgVLtEYYYeP8NiJRL_XP8SCD4ebU_cJMLDWgq1DgxpfqIBz0fCNe0ty9LwEhMhmP6OpmU5mRAGzXNKOp_lYYEVmvRoEwcRyFdM1W4N61eu8EYGyfzTrH1gfG8FMLfQ",
+  "dq": "uC9tAhQI7vRE2isCeoxY3YgJd2RBOdRrIjowUoS7WuEho_aLKWgoJDSzZoXad9pindjaSRj9CRKZ7oRW1Dp0NQwTQdUsyZSZt4cB5yxg50ty5IE81XDX0pgyRCJOXNpKxSZYtcBUzqlId4dCRd2codqaKJkP77K0TmWlQve_CoCPuaSP4CGqt8GpQj1fKzI_NYYqNVk-1jSAa9PZ2E-gW0iJwQoUNkDRTBgIVLx1qu7aYieYeif8oTFjKGKkP3EXCTGXKSdLd729-37NgWR0YktLFQsWfiNHcy1_-eWI6HKc8onOv5IuchKQ9nmQIE1mQUZBATCtx-vdzQqF_alTKw",
+  "e": "AQAB",
+  "key_ops": [
+    "unwrapKey"
+  ],
+  "kid": "jsMQHFiA2uMGtiG3Ro8RJnYdEhp5W5KjGW-Vcf3-YMk",
+  "kty": "RSA",
+  "n": "zEi7NpGkzGyG2-PDWTy72hvti-pGBZLidxbk10_fenjzOavKcO5yGXSCfX_Xl0-WYaJfb7Kz6CRRtnwBGx8mrsftodtxt3kdrFnf6chQ2JJ5dmnz_ErIbHjHaFlxXvEqv3ivqIQSZvuns8QJip8RGQ63g7nmPDyvBcvLMFUtnXy7Y6rzUI4HO2eeg8htMPCWN5-L7Ol5_IT11NuZK6J3_UN1B0P7fFGdVMhsNR5D_jHOD-U4jZQijyVgzIXxwN_vnf90e5_ZUgsLypwh2DT7qzhqES8hzIIk_Cjs5mCUwDjfiBh0g7LNjXaj0b7rAn2X6yPuMK6zYi_FXJYSCQ7LH3THi_h5r2--xQ2h40He23JjfNpEGu98uMgB_dvzH0Dco8OKh4Aj2wjLpZVFOS3Zpu-WUalP-ecEiZ6nzmCMoIpWM0U4t4tpGwC1X-MWNj75zYLI8-yd3ELVBx3PQSzDukGOVLBdtpF0txf7Np0i9RFRW-nxf3xPVFCbZYbc87GMZy1CeDT2NBJJHtQaPKBismnEXtXtiM7aJDRM74F5JXjDVR62eHGxFCy46oyI_NS3FmvkoVtV8hvbXBRMSU8sNeOrVKnbWpfjBGFGD8VbssKoAAdMfn6usOiiH71SBg-L9KoBwdRfu7XFE-WbSA2d03hMvEPJWk9srh5qGdtQ_QE",
+  "p": "_RRumCEIUoM1gipAOIeQZ_td0E2qx20bFHArBf1V7ev4v4S22tt1Bt9oa20KPfA6mokycaB8MBCX7LTRvBT1XwSh4sU2TptuibElrarQvFoJIVGqugqG4GxsBpZpy-Dvx9NJtMTjiiGXSn87-_8iSWrPlc4Vdbgb8S9Vz2x_Mp-uSwZaFOO3Gx4miD29y1ugE26hFF0RdgBDJnJEk71W9NG2D_MbnBBa3DrPesdU8rX08RurpV8C1TVUgelDxUBQUxrRXRaHOPbrwPBBRDOgzNlFiuzUdkw6qwlfhp9W1tn3j7uAnqMHxnaPhJJ51tFjd_7MgSawmmM415c4NWa5xw",
+  "q": "zqQpmsvtjeU486C2DZ_NjsDHanYXgfxiY3X25XpM0ojLrtIQWKUaFxlggJGSQzQ6QJqB-5sJu9EEE4DGZ3UYoakY_hzxv0uHOHrI20ohFgF5K3YQdqGRhC3Bo66ezpmD3FT1EPXQM2OLEAi82lUb7UrbP_DSU0BmYOd6kPd1a3FD2qm0DjA15IYW5vUHNisx79W3kt_8s7ZICjiVpvJTSa1TVIX3wPM0qLHOWbHEOswLENC4fZ7EFYkNr3MvtAOxLcJ_mi4zzhAkyGdKJbTQXt6bhD_gWL5K1iOFM5Vg_NRTjUMqndDK33KDYZhC-MexSR-IaN7MbP1Y787uFL5S9w",
+  "qi": "DHzvGmAC4OZIfvDBkqXrvrvBeqURfioiEN72SL6oPcBqof899UL50BVohU0a2IeydwLSSff1i0U0jfX8ss_YKnCw2MxH3eNdQ-JXAQp7sefYLiFKPlGQnaxCNL0Z3kDatPhJnfTHaJA_UKtuPtcIINK5lJH80Iqqg3rl1p5G84ujbIghcgJXGoRGjJfkpUQgarTwPzdScR8Xo-YmN4dNav2X76s5toTJbSc4wLh3z_vdgL4GTGv9jzRKBPJcv07SDOFz92VA2r2DiIPQxg1eAKq8MPwEQ_bkaqh6Sk57_-JT8CRN0vfZbHw_3jCc8ijqRQE-4HaKcVCAbgnTzgr8cw"
+}
\ No newline at end of file
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
index 68711b85676591f5df52ea08e1c89797ff093ec3..00302464512b983ae1f7116a8bdbe620e9f11069 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
@@ -47,12 +47,6 @@ public class SubmissionSender implements Sender {
         this.keyService = keyService;
     }
 
-    @Override
-    public ValidationResult validatePublicKey(final RSAKey publicKey) {
-        LOGGER.info("Validating public key integrity");
-        return validationService.validateEncryptionPublicKey(publicKey);
-    }
-
     @Override
     public ValidationResult validateMetadata(final Metadata metadata) {
         LOGGER.info("Validating metadata");
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
index ddc012016dfcdea8efba56b0699ea053009218eb..3de5c7e0b9df2423b624cacec996d3d624782392 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
@@ -2,12 +2,19 @@ package dev.fitko.fitconnect.core;
 
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.jwt.SignedJWT;
+import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.RestApiException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
@@ -44,7 +51,7 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent) {
+    public byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent) throws DecryptionException {
         LOGGER.info("Decrypting string");
         return cryptoService.decryptToBytes(privateKey, encryptedContent);
     }
@@ -62,32 +69,50 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public Submission getSubmission(final UUID submissionId) {
+    public Submission getSubmission(final UUID submissionId) throws RestApiException {
         LOGGER.info("Loading submission {}", submissionId);
         return submissionService.getSubmission(submissionId);
     }
 
     @Override
-    public String fetchAttachment(final UUID submissionId, final UUID attachmentId) {
+    public String fetchAttachment(final UUID submissionId, final UUID attachmentId) throws RestApiException {
         LOGGER.info("Loading attachment {} for submission {}", attachmentId, submissionId);
         return submissionService.getAttachment(submissionId, attachmentId);
     }
 
     @Override
-    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) {
+    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException {
         LOGGER.info("Loading event log for destination {}", destinationId);
         return eventLogService.getEventLog(caseId, destinationId);
     }
+    @Override
+    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) {
+        LOGGER.info("Loading authentication tags of {} event for submission {}", event, submission.getSubmissionId());
+        return eventLogService.getAuthenticationTagsForEvent(event, submission);
+    }
+
+    @Override
+    public ValidationResult validateMetadata(final Metadata metadata, final Submission submission, final AuthenticationTags authenticationTags) {
+        LOGGER.info("Validating metadata");
+        final Destination destination = submissionService.getDestination(submission.getDestinationId());
+        return validationService.validateMetadata(metadata, submission, destination, authenticationTags);
+    }
+
+    @Override
+    public ValidationResult validateData(final byte[] data, final Submission submission, final Metadata metadata, final AuthenticationTags authenticationTags) {
+        LOGGER.info("Validating data");
+        return validationService.validateData(data, submission, metadata, authenticationTags);
+    }
 
     @Override
-    public ValidationResult validateMetadata(final Metadata metadata) {
-        LOGGER.info("Validating metadata schema");
-        return validationService.validateMetadataSchema(metadata);
+    public ValidationResult validateAttachments(final List<AttachmentForValidation> attachmentsForValidation, final AuthenticationTags authenticationTags) {
+        LOGGER.info("Validating attachments");
+        return validationService.validateAttachments(attachmentsForValidation, authenticationTags);
     }
 
     @Override
     public ValidationResult validateHashIntegrity(final String originalHash, final byte[] data) {
-        LOGGER.info("Validating data hash integrity");
+        LOGGER.info("Validating hash integrity");
         return validationService.validateHashIntegrity(originalHash, data);
     }
 
@@ -98,7 +123,7 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public void acceptSubmission(final EventPayload eventPayload) {
+    public void acceptSubmission(final EventPayload eventPayload) throws RestApiException, EventCreationException {
         LOGGER.info("Accepting submission");
         final SignedJWT confirmedSubmissionEvent = securityEventService.createAcceptSubmissionEvent(eventPayload);
         eventLogService.sendEvent(eventPayload.getCaseId(), confirmedSubmissionEvent.serialize());
@@ -106,7 +131,7 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public void rejectSubmission(final EventPayload eventPayload) {
+    public void rejectSubmission(final EventPayload eventPayload) throws RestApiException, EventCreationException  {
         LOGGER.info("Rejecting submission");
         final SignedJWT rejectSubmissionEvent = securityEventService.createRejectSubmissionEvent(eventPayload);
         eventLogService.sendEvent(eventPayload.getCaseId(), rejectSubmissionEvent.serialize());
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
index b063236ba28c92ff979131826681e6e7daa57bfd..e43b59f16f73efdedac896c74805bb1d34717424 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
@@ -2,7 +2,13 @@ package dev.fitko.fitconnect.core.crypto;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.nimbusds.jose.*;
+import com.nimbusds.jose.CompressionAlgorithm;
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWEHeader;
+import com.nimbusds.jose.JWEObject;
+import com.nimbusds.jose.Payload;
 import com.nimbusds.jose.crypto.RSADecrypter;
 import com.nimbusds.jose.crypto.RSAEncrypter;
 import com.nimbusds.jose.jwk.RSAKey;
@@ -78,7 +84,7 @@ public class JWECryptoService implements CryptoService {
             jwe.decrypt(new RSADecrypter(privateKey));
             LOGGER.info("Decrypting {} bytes took {} ", encData.getBytes().length, StopWatch.stopWithFormattedTime(start));
             return jwe.getPayload();
-        } catch (final ParseException | JOSEException e) {
+        } catch (final ParseException | IllegalStateException | JOSEException e) {
             throw new DecryptionException(e.getMessage(), e);
         }
     }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
index cd211de6cab748b1bf27484468c68ae915cbb043..c6e49cbb155f6980f66e8908378c67077e6e8d05 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
@@ -2,18 +2,20 @@ package dev.fitko.fitconnect.core.events;
 
 import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLog;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
 import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
 import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
-import dev.fitko.fitconnect.core.util.EventLogUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpEntity;
@@ -28,6 +30,13 @@ import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
+import static dev.fitko.fitconnect.core.util.EventLogUtil.eventEqualsSubmissionId;
+import static dev.fitko.fitconnect.core.util.EventLogUtil.eventToEntry;
+import static dev.fitko.fitconnect.core.util.EventLogUtil.getAuthenticationTags;
+import static dev.fitko.fitconnect.core.util.EventLogUtil.getJWTSFromEvents;
+import static dev.fitko.fitconnect.core.util.EventLogUtil.getJwtsFromEvents;
+import static dev.fitko.fitconnect.core.util.EventLogUtil.mapEventLogToEntries;
+
 public class EventLogApiService implements EventLogService {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(EventLogApiService.class);
@@ -48,37 +57,59 @@ public class EventLogApiService implements EventLogService {
     }
 
     @Override
-    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) {
+    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws EventLogException {
         final EventLog eventLog = loadEventLog(caseId);
-        final List<SignedJWT> events = EventLogUtil.getJWTSFromEvents(eventLog.getEventLogs());
+        final List<SignedJWT> events = getJWTSFromEvents(eventLog.getEventLogs());
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
-        validateLog(eventLogVerifier.validateEventLogs(ctx, events));
-        return EventLogUtil.mapEventLogToEntries(eventLog);
+        evaluateLogValidation(eventLogVerifier.validateEventLogs(ctx, events));
+        return mapEventLogToEntries(eventLog);
+    }
+
+    @Override
+    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) throws EventLogException {
+
+        final EventLog eventLog = loadEventLog(submission.getCaseId());
+        final List<SignedJWT> submitEvents = getJwtsFromEvents(event, submission, eventLog);
+
+        if (submitEvents.size() != 1) {
+           throw new SubmitEventNotFoundException("Event log does not contain exactly one submit event");
+        }
+
+        final SignedJWT submitEvent = submitEvents.stream().findFirst().get();
+        final AuthenticationTags authenticationTags = getAuthenticationTags(submitEvent);
+
+        if(authenticationTags.getMetadata() == null && authenticationTags.getData() == null){
+            throw new AuthenticationTagsEmptyException("Authentication tags are empty");
+        }
+
+        verifyEventWithAuthTags(submission, submitEvent, authenticationTags);
+
+        return authenticationTags;
     }
 
     @Override
-    public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId, final AuthenticationTags authenticationTags) throws RestApiException, EventLogException {
+    public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId, final AuthenticationTags authenticationTags) throws EventLogException {
         final EventLog eventLog = loadEventLog(caseId);
-        final SignedJWT latestEvent = getLatestEventForSubmission(submissionId, eventLog);
+        final SignedJWT latestEvent = getLatestEventForSubmission(submissionId, eventLog.getEventLogs());
         if (latestEvent == null) {
             LOGGER.info("No events found for submission {}", submissionId);
             return new SubmissionStatus();
         }
         final var ctx = new ValidationContext(destinationId, caseId, authenticationTags);
-        validateLog(eventLogVerifier.validateEventLogs(ctx, List.of(latestEvent)));
-        final EventLogEntry eventLogEntry = EventLogUtil.eventToEntry(latestEvent);
+        evaluateLogValidation(eventLogVerifier.validateEventLogs(ctx, List.of(latestEvent)));
+        final EventLogEntry eventLogEntry = eventToEntry(latestEvent);
         return new SubmissionStatus(eventLogEntry.getEvent().getState(), eventLogEntry.getProblems());
     }
 
     @Override
-    public void sendEvent(final UUID caseId, final String signedAndSerializedSET) {
+    public void sendEvent(final UUID caseId, final String signedAndSerializedSET) throws EventLogException {
         final String url = config.getEventsEndpoint();
         final HttpHeaders headers = getHttpHeaders("application/jose");
         final HttpEntity<String> entity = new HttpEntity<>(signedAndSerializedSET, headers);
         try {
             restTemplate.exchange(url, HttpMethod.POST, entity, Void.class, caseId);
         } catch (final RestClientException e) {
-            throw new RestApiException("Sending event failed", e);
+            throw new EventLogException("Sending event failed", e);
         }
     }
 
@@ -89,7 +120,7 @@ public class EventLogApiService implements EventLogService {
         try {
             return restTemplate.exchange(url, HttpMethod.GET, entity, EventLog.class, caseId).getBody();
         } catch (final RestClientException e) {
-            throw new RestApiException("EventLog query failed", e);
+            throw new EventLogException("EventLog query failed", e);
         }
     }
 
@@ -109,21 +140,33 @@ public class EventLogApiService implements EventLogService {
         return headers;
     }
 
-    private SignedJWT getLatestEventForSubmission(final UUID submissionId, final EventLog eventLog) {
-        return EventLogUtil.getJWTSFromEvents(eventLog.getEventLogs()).stream()
-                .filter(EventLogUtil.eventEqualsSubmissionId(submissionId))
+    private SignedJWT getLatestEventForSubmission(final UUID submissionId, final List<String> events) {
+        return getJWTSFromEvents(events).stream()
+                .filter(eventEqualsSubmissionId(submissionId))
                 .reduce((first, second) -> second)
                 .orElse(null);
     }
 
-    private void validateLog(final List<ValidationResult> results) {
-        if (!results.isEmpty()) {
-            final String errorMessages = results.stream()
+    private void verifyEventWithAuthTags(final Submission submission, final SignedJWT submitEvent, final AuthenticationTags authenticationTags) {
+
+        final var ctx = new ValidationContext(submission.getDestinationId(), submission.getCaseId(), authenticationTags);
+        final List<ValidationResult> validationResults = eventLogVerifier.validateEventLogs(ctx, List.of(submitEvent));
+
+        if(!validationResults.isEmpty()){
+            final String errorMessages = validationResults.stream()
                     .map(ValidationResult::getError)
-                    .map(Throwable::getMessage)
-                    .distinct()
+                    .map(Exception::getMessage)
                     .collect(Collectors.joining("\n"));
-            throw new EventLogException("Event log contains invalid entries: \n" + errorMessages);
+            throw new EventLogException(errorMessages);
         }
     }
+
+    private void evaluateLogValidation(final List<ValidationResult> results) {
+        results.stream()
+                .filter(ValidationResult::hasError)
+                .findFirst()
+                .ifPresent((result) -> {
+                    throw new EventLogException(result.getError().getMessage(), result.getError());
+                });
+    }
 }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
index 905a5d52a3cb65adaa5753c0816da5b614863c53..b097f861baf262b3845c74a4030d166688f985c0 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
@@ -5,6 +5,7 @@ import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
 import com.nimbusds.jose.JWSVerifier;
 import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.SignedJWT;
@@ -99,12 +100,12 @@ public class EventLogVerifier implements EventLogVerificationService {
         if (header.getAlgorithm() == null) {
             ctx.addError("The provided alg in the SET header must not be null.");
         } else {
-            ctx.addResult(header.getAlgorithm() == JWSAlgorithm.PS512, "The provided alg in the SET header is not allowed.");
+            ctx.addErrorIfTestFailed(header.getAlgorithm() == JWSAlgorithm.PS512, "The provided alg in the SET header is not allowed.");
         }
         if (header.getType() == null) {
             ctx.addError("The provided typ in the SET header must not be null.");
         } else {
-            ctx.addResult(header.getType().toString().equals(HEADER_TYPE), "The provided typ in the SET header is not " + HEADER_TYPE);
+            ctx.addErrorIfTestFailed(header.getType().toString().equals(HEADER_TYPE), "The provided typ in the SET header is not " + HEADER_TYPE);
         }
     }
 
@@ -122,16 +123,16 @@ public class EventLogVerifier implements EventLogVerificationService {
     }
 
     private void validatePayload(final ValidationContext ctx, final JWTClaimsSet claims) throws ParseException {
-        ctx.addResult(claims.getClaim(ISSUER) != null, "The claim iss is missing in the payload of the SET.");
-        ctx.addResult(claims.getClaim(ISSUED_AT) != null, "The claim iat is missing in the payload of the SET.");
-        ctx.addResult(claims.getClaim(JWT_ID) != null, "The claim jti is missing in the payload of the SET.");
-        ctx.addResult(claims.getClaim(SUBJECT) != null, "The claim sub is missing in the payload of the SET.");
-        ctx.addResult(claims.getClaim(CLAIM_TXN) != null, "The claim txn is missing in the payload of the SET.");
-        ctx.addResult(claims.getClaim(CLAIM_EVENTS) != null, "The claim events is missing in the payload of the SET.");
-        ctx.addResult(claims.getJSONObjectClaim(CLAIM_EVENTS).keySet().size() == 1, "The events claims has must be exactly one event.");
-        ctx.addResult(claims.getStringClaim("sub").matches("(submission|case|reply):" + UUID_V4_PATTERN), "The provided subject does not match the allowed pattern.");
-        ctx.addResult(claims.getStringClaim(CLAIM_TXN).matches("case:" + UUID_V4_PATTERN), "The provided txn does not match the allowed pattern.");
-        getEventClaim(claims).ifPresentOrElse(event -> ctx.addResult(Event.fromSchemaUri(event) != null, "The provided event is not a valid event supported by this instance."),
+        ctx.addErrorIfTestFailed(claims.getClaim(ISSUER) != null, "The claim iss is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getClaim(ISSUED_AT) != null, "The claim iat is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getClaim(JWT_ID) != null, "The claim jti is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getClaim(SUBJECT) != null, "The claim sub is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getClaim(CLAIM_TXN) != null, "The claim txn is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getClaim(CLAIM_EVENTS) != null, "The claim events is missing in the payload of the SET.");
+        ctx.addErrorIfTestFailed(claims.getJSONObjectClaim(CLAIM_EVENTS).keySet().size() == 1, "The events claims has must be exactly one event.");
+        ctx.addErrorIfTestFailed(claims.getStringClaim("sub").matches("(submission|case|reply):" + UUID_V4_PATTERN), "The provided subject does not match the allowed pattern.");
+        ctx.addErrorIfTestFailed(claims.getStringClaim(CLAIM_TXN).matches("case:" + UUID_V4_PATTERN), "The provided txn does not match the allowed pattern.");
+        getEventClaim(claims).ifPresentOrElse(event -> ctx.addErrorIfTestFailed(Event.fromSchemaUri(event) != null, "The provided event is not a valid event supported by this instance."),
                 () -> ctx.addError("No events in JWT"));
     }
 
@@ -139,7 +140,7 @@ public class EventLogVerifier implements EventLogVerificationService {
         if (signatureKey == null) {
             ctx.addError("The signature key cannot be validated, it must not be null.");
         } else {
-            ctx.addResult(validationService.validateSignaturePublicKey(signatureKey));
+            ctx.addResult(validationService.validatePublicKey(signatureKey, KeyOperation.VERIFY));
         }
     }
 
@@ -148,7 +149,7 @@ public class EventLogVerifier implements EventLogVerificationService {
             ctx.addError("The signature cannot validated, signature key is unavailable.");
         } else {
             final JWSVerifier jwsVerifier = new RSASSAVerifier(signatureKey);
-            ctx.addResult(signedJWT.verify(jwsVerifier), "The signature of the token could not be verified with the specified key.");
+            ctx.addErrorIfTestFailed(signedJWT.verify(jwsVerifier), "The signature of the token could not be verified with the specified key.");
         }
     }
 
@@ -177,15 +178,15 @@ public class EventLogVerifier implements EventLogVerificationService {
             ctx.addError("The event '" + event.getSchemaUri() + "' has to be created by the destination ('iss' claim must be an UUID)");
         }
         if (destinationId != null) {
-            ctx.addResult(destinationId.equals(ctx.getDestinationId()), "The destination of the submission is not the issuer ('iss' claim must match submission.destinationId)");
+            ctx.addErrorIfTestFailed(destinationId.equals(ctx.getDestinationId()), "The destination of the submission is not the issuer ('iss' claim must match submission.destinationId)");
         }
     }
 
     private void validateAuthenticationTags(final ValidationContext ctx, final JWTClaimsSet payload) throws ParseException {
         final AuthenticationTags eventAuthTags = getAuthenticationTags(payload);
         final AuthenticationTags submissionAuthTags = ctx.getAuthenticationTags();
-        ctx.addResult(eventAuthTags != null, "AuthenticationTags of event must not be null.");
-        ctx.addResult(submissionAuthTags != null, "AuthenticationTags of submission must not be null.");
+        ctx.addErrorIfTestFailed(eventAuthTags != null, "AuthenticationTags of event must not be null.");
+        ctx.addErrorIfTestFailed(submissionAuthTags != null, "AuthenticationTags of submission must not be null.");
         if (eventAuthTags != null && submissionAuthTags != null) {
             validateDataAuthTags(ctx, eventAuthTags.getData(), submissionAuthTags.getData());
             validateMetadataAuthTags(ctx, eventAuthTags.getMetadata(), submissionAuthTags.getMetadata());
@@ -195,20 +196,20 @@ public class EventLogVerifier implements EventLogVerificationService {
 
     private void validateAttachmentsAuthTags(final ValidationContext ctx, final Map<UUID, String> eventAttachmentTags, final Map<UUID, String> submissionAttachmentTags) {
         if (eventAttachmentTags != null && submissionAttachmentTags != null) {
-            ctx.addResult(eventAttachmentTags.size() == submissionAttachmentTags.size(), "The events quantity of attachments does not match the submission.");
+            ctx.addErrorIfTestFailed(eventAttachmentTags.size() == submissionAttachmentTags.size(), "The events quantity of attachments does not match the submission.");
             eventAttachmentTags.forEach((key, eventTag) -> {
                 final String submissionTag = submissionAttachmentTags.get(key);
-                ctx.addResult(eventTag.equals(submissionTag), "The authentication-tag for the attachment " + key + " does not match the submission.");
+                ctx.addErrorIfTestFailed(eventTag.equals(submissionTag), "The authentication-tag for the attachment " + key + " does not match the submission.");
             });
         }
     }
 
     private void validateDataAuthTags(final ValidationContext ctx, final String eventDataAuthTag, final String submissionDataAuthTag) {
-        ctx.addResult(eventDataAuthTag.equals(submissionDataAuthTag), "The events data authentication-tag does not match the submission.");
+        ctx.addErrorIfTestFailed(eventDataAuthTag.equals(submissionDataAuthTag), "The events data authentication-tag does not match the submission.");
     }
 
     private void validateMetadataAuthTags(final ValidationContext ctx, final String eventMetadataAuthTag, final String submissionMetadataAuthTag) {
-        ctx.addResult(eventMetadataAuthTag.equals(submissionMetadataAuthTag), "The events metadata authentication-tag does not match the submission.");
+        ctx.addErrorIfTestFailed(eventMetadataAuthTag.equals(submissionMetadataAuthTag), "The events metadata authentication-tag does not match the submission.");
     }
 
     private static boolean eventContainsAuthTags(final JWTClaimsSet payload) throws ParseException {
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java b/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
index c6054dc66a485d3ed0e45047cc986489b5112f68..0191811982550a7774f4cea128b76a09de31eeec 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
@@ -21,6 +21,7 @@ import dev.fitko.fitconnect.api.services.events.SecurityEventService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
 
 import java.text.ParseException;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -123,6 +124,9 @@ public class SecurityEventTokenService implements SecurityEventService {
     }
 
     private Map<UUID, String> buildAttachmentAuthenticationTags(final Map<UUID, String> encryptedAttachments) {
+        if(encryptedAttachments == null || encryptedAttachments.isEmpty()){
+            return Collections.emptyMap();
+        }
         return encryptedAttachments
                 .entrySet()
                 .stream()
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java b/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
index 2ab47f490335b6d9aee31e5e76e7274b0867de14..2445d98c39707200eb5bbc8c080cda126bce8d05 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
@@ -2,6 +2,7 @@ package dev.fitko.fitconnect.core.keys;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
@@ -107,12 +108,12 @@ public class PublicKeyService implements KeyService {
     }
 
     private void validateEncryptionKey(final RSAKey rsaKey) {
-        final ValidationResult result = validationService.validateEncryptionPublicKey(rsaKey);
+        final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
         validateResult(result, "Invalid public encryption key");
     }
 
     private void validateSignatureKey(final RSAKey rsaKey) {
-        final ValidationResult result = validationService.validateSignaturePublicKey(rsaKey);
+        final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.VERIFY);
         validateResult(result, "Public signature key is not valid");
     }
 
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java b/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
index d940c7ccbb2d78bc37f61003138e5b6b16ac2bf4..9af4798e4282d65266f8e255dc3dc8d54206efc1 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
@@ -19,7 +19,9 @@ import lombok.Setter;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.client.RestClientException;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -42,7 +44,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public Destination getDestination(final UUID destinationID) {
+    public Destination getDestination(final UUID destinationID) throws RestApiException {
         final RequestSettings requestSettings = RequestSettings.builder()
                 .url(config.getDestinationsEndpoint())
                 .method(HttpMethod.GET)
@@ -55,7 +57,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public SubmissionForPickup announceSubmission(final CreateSubmission submission) {
+    public SubmissionForPickup announceSubmission(final CreateSubmission submission) throws RestApiException {
         final RequestSettings requestSettings = RequestSettings.builder()
                 .url(config.getSubmissionsEndpoint())
                 .method(HttpMethod.POST)
@@ -68,7 +70,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) {
+    public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) throws RestApiException {
         final Map<String, Object> params = new HashMap<>();
         params.put("submissionId", submissionId);
         params.put("attachmentId", attachmentId);
@@ -84,7 +86,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public String getAttachment(final UUID submissionId, final UUID attachmentId) {
+    public String getAttachment(final UUID submissionId, final UUID attachmentId) throws RestApiException {
         final Map<String, Object> params = new HashMap<>();
         params.put("submissionId", submissionId);
         params.put("attachmentId", attachmentId);
@@ -100,7 +102,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public Submission sendSubmission(final SubmitSubmission submission) {
+    public Submission sendSubmission(final SubmitSubmission submission) throws RestApiException {
         final Map<String, Object> params = Map.of("submissionId", submission.getSubmissionId());
         final RequestSettings requestSettings = RequestSettings.builder()
                 .url(config.getSubmissionEndpoint())
@@ -114,7 +116,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public SubmissionsForPickup pollAvailableSubmissions(final int offset, final int limit) {
+    public SubmissionsForPickup pollAvailableSubmissions(final int offset, final int limit) throws RestApiException {
         final String urlWithQueryParams = UriComponentsBuilder.fromHttpUrl(config.getSubmissionsEndpoint())
                 .queryParam("limit", limit)
                 .queryParam("offset", offset)
@@ -149,7 +151,7 @@ public class SubmissionApiService implements SubmissionService {
     }
 
     @Override
-    public Submission getSubmission(final UUID submissionId) {
+    public Submission getSubmission(final UUID submissionId) throws RestApiException {
         final Map<String, Object> params = Map.of("submissionId", submissionId);
         final RequestSettings requestSettings = RequestSettings.builder()
                 .url(config.getSubmissionEndpoint())
@@ -169,12 +171,19 @@ public class SubmissionApiService implements SubmissionService {
         final Class<T> responseType = requestSettings.responseType;
         final Map<String, ?> params = requestSettings.params;
         try {
-            return restTemplate.exchange(url, method, entity, responseType, params).getBody();
+            return evaluateStatusCode(restTemplate.exchange(url, method, entity, responseType, params));
         } catch (final RestClientException e) {
             throw new RestApiException(e.getMessage(), e);
         }
     }
 
+    private <T> T evaluateStatusCode(final ResponseEntity<T> response) {
+        if(response.getStatusCode().equals(HttpStatus.NOT_FOUND)){
+            throw new RestApiException(response.getStatusCode(), "requested entity could not be found");
+        }
+        return response.getBody();
+    }
+
     private HttpEntity<String> getHttpEntity(final HttpHeaders headers) {
         return getHttpEntity(null, headers);
     }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java b/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
index 875133cc2011a6d9e3d9ea584d7617d97dff888f..aad6d08a97b22e227f72124789621887b73ae69f 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
@@ -4,19 +4,31 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.SignedJWT;
-import dev.fitko.fitconnect.api.domain.model.event.*;
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.domain.model.event.Event;
+import dev.fitko.fitconnect.api.domain.model.event.EventClaimFields;
+import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
+import dev.fitko.fitconnect.api.domain.model.event.EventLog;
+import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
+import dev.fitko.fitconnect.api.domain.model.event.TypeAndUUID;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.exceptions.EventCreationException;
 import dev.fitko.fitconnect.api.exceptions.EventLogException;
 
 import java.text.ParseException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
-import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.*;
+import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.DESTINATION;
+import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.SUBMISSION_SERVICE;
+import static dev.fitko.fitconnect.api.domain.model.event.EventIssuer.UNDEFINED;
 
 public final class EventLogUtil {
 
@@ -79,8 +91,8 @@ public final class EventLogUtil {
         final String eventName = eventEntry.getKey();
         final Map<String, Object> eventPayload = eventEntry.getValue();
         final List<Problem> problems = new ArrayList<>();
-        if (eventPayload.containsKey("problems")) {
-            problems.addAll((List<Problem>) eventPayload.get("problems"));
+        if (eventPayload.containsKey(EventClaimFields.PROBLEMS)) {
+            problems.addAll((List<Problem>) eventPayload.get(EventClaimFields.PROBLEMS));
         }
         return EventLogEntry.builder()
                 .event(Event.fromSchemaUri(eventName))
@@ -88,7 +100,7 @@ public final class EventLogUtil {
                 .issueTime(claimsSet.getIssueTime())
                 .eventId(getEventId(claimsSet))
                 .caseId(getTransactionClaim(claimsSet))
-                .submissionId(getSubmissionId(claimsSet))
+                .submissionId(getSubmissionIdFromJWT(claimsSet))
                 .problems(problems)
                 .build();
     }
@@ -107,10 +119,14 @@ public final class EventLogUtil {
         }
     }
 
-    private static UUID getSubmissionId(final JWTClaimsSet claimsSet) {
+    public static UUID getSubmissionIdFromJWT(final JWTClaimsSet claimsSet) {
         return TypeAndUUID.fromString(claimsSet.getSubject()).getUuid();
     }
 
+    public static UUID getSubmissionIdFromJWT(final SignedJWT signedJWT) {
+        return TypeAndUUID.fromString(getClaimsSet(signedJWT).getSubject()).getUuid();
+    }
+
     private static UUID getEventId(final JWTClaimsSet claimsSet) {
         return UUID.fromString(claimsSet.getJWTID());
     }
@@ -139,18 +155,38 @@ public final class EventLogUtil {
         }
     }
 
+    public static AuthenticationTags getAuthenticationTags(final SignedJWT jwt) {
+       return getAuthenticationTags(getClaimsSet(jwt));
+    }
+
     public static AuthenticationTags getAuthenticationTags(final JWTClaimsSet claims) {
         try {
             final Map<String, Object> eventsClaim = (Map) claims.getClaim(EventClaimFields.CLAIM_EVENTS);
             final Map.Entry<String, Map> eventEntry = (Map.Entry) eventsClaim.entrySet().iterator().next();
             final Map<String, Object> events = eventEntry.getValue();
-            final String authTags = MAPPER.writeValueAsString(events.get("authenticationTags"));
+            final String authTags = MAPPER.writeValueAsString(events.get(EventClaimFields.AUTHENTICATION_TAGS));
             return MAPPER.readValue(authTags, AuthenticationTags.class);
         } catch (final JsonProcessingException e) {
             throw new EventCreationException(e.getMessage(), e);
         }
     }
 
+    public static String getAuthenticationTagFromEncryptedData(final String encryptedData) {
+        try {
+            final String[] parts = encryptedData.split(ApplicationConfig.AUTH_TAG_SPLIT_TOKEN);
+            return parts[parts.length - 1];
+        } catch (final Exception e) {
+            return null;
+        }
+    }
+
+    public static List<SignedJWT> getJwtsFromEvents(final Event event, final Submission submission, final EventLog eventLog) {
+        return getJWTSFromEvents(eventLog.getEventLogs()).stream()
+                .filter(jwt -> getSubmissionIdFromJWT(jwt).equals(submission.getSubmissionId()))
+                .filter(jwt -> getEventFromJWT(jwt).equals(event))
+                .collect(Collectors.toList());
+    }
+
     public static Predicate<SignedJWT> eventEqualsSubmissionId(final UUID submissionId) {
         return jwt -> {
             try {
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
index 3f666e9629185d1bbbb2ba016e3361190f7a245c..9bfa9535a8cb903e6fa3faa52973a5e02a7ed2de 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
@@ -1,5 +1,6 @@
 package dev.fitko.fitconnect.core.validation;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JacksonException;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -11,8 +12,26 @@ import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.jose.util.Base64;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.destination.DestinationService;
 import dev.fitko.fitconnect.api.domain.model.event.EventClaimFields;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.AttachmentHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.IncorrectAttachmentAuthenticationTag;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataJsonSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataXmlSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAuthenticationTag;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.*;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
+import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
+import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
+import dev.fitko.fitconnect.api.domain.model.replychannel.ReplyChannel;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
 import dev.fitko.fitconnect.api.exceptions.DataIntegrityException;
 import dev.fitko.fitconnect.api.exceptions.RootCertificateException;
@@ -47,20 +66,20 @@ import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import static dev.fitko.fitconnect.core.util.EventLogUtil.getAuthenticationTagFromEncryptedData;
 import static dev.fitko.fitconnect.jwkvalidator.JWKValidator.withX5CValidation;
 import static dev.fitko.fitconnect.jwkvalidator.JWKValidator.withoutX5CValidation;
+import static java.util.Objects.isNull;
 
 public class DefaultValidationService implements ValidationService {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultValidationService.class);
-    private static final ObjectMapper MAPPER = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
+    private static final ObjectMapper MAPPER = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd")).setSerializationInclusion(JsonInclude.Include.NON_NULL);
     private static final JsonSchemaFactory SCHEMA_FACTORY_DRAFT_2020 = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
-
     private static final JsonSchemaFactory SCHEMA_FACTORY_DRAFT_2007 = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
     public static final String VALID_SCHEMA_URL_EXPRESSION = "https://schema\\.fitko\\.de/fit-connect/metadata/1\\.\\d+\\.\\d+/metadata.schema.json";
 
@@ -78,19 +97,9 @@ public class DefaultValidationService implements ValidationService {
     }
 
     @Override
-    public ValidationResult validateEncryptionPublicKey(final RSAKey publicKey) {
+    public ValidationResult validatePublicKey(final RSAKey publicKey, final KeyOperation keyOperation) {
         try {
-            validateKey(publicKey, KeyOperation.WRAP_KEY);
-        } catch (final Exception e) {
-            return ValidationResult.error(e);
-        }
-        return ValidationResult.ok();
-    }
-
-    @Override
-    public ValidationResult validateSignaturePublicKey(final RSAKey signatureKey) {
-        try {
-            validateKey(signatureKey, KeyOperation.VERIFY);
+            validateKey(publicKey, keyOperation);
         } catch (final Exception e) {
             return ValidationResult.error(e);
         }
@@ -116,7 +125,9 @@ public class DefaultValidationService implements ValidationService {
     public ValidationResult validateMetadataSchema(final Metadata metadata) {
 
         if (metadata.getSchema() != null && !metadata.getSchema().isEmpty() && !metadata.getSchema().matches(VALID_SCHEMA_URL_EXPRESSION)) {
-            return ValidationResult.error(new ValidationException("The provided metadata schema is not supported."));
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#required-schema-reference
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#metadatenschema
+            return ValidationResult.problem(new UnsupportedMetadataSchema(metadata.getSchema()));
         }
 
         try {
@@ -124,7 +135,8 @@ public class DefaultValidationService implements ValidationService {
             final JsonNode inputNode = MAPPER.readTree(metadataJson);
             return validate2020JsonSchema(schemaProvider.loadMetadataSchema(config.getMetadataSchemaWriteVersion()), inputNode);
         } catch (final JsonProcessingException e) {
-            return ValidationResult.error(e);
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#schema-pr%C3%BCfung
+            return ValidationResult.withErrorAndProblem(e, new MetadataSchemaViolation());
         }
     }
 
@@ -146,7 +158,7 @@ public class DefaultValidationService implements ValidationService {
             final byte[] originalHash = messageDigestService.fromHexString(originalHexHash);
             final boolean hashesAreNotEqual = !messageDigestService.verify(originalHash, data);
             if (hashesAreNotEqual) {
-                return ValidationResult.error(new DataIntegrityException("Hash sum of transmitted data does not equal the hash of the sender."));
+                return ValidationResult.error(new DataIntegrityException("Metadata contains invalid hash value"));
             }
             return ValidationResult.ok();
         } catch (final IllegalArgumentException | NullPointerException e) {
@@ -154,6 +166,7 @@ public class DefaultValidationService implements ValidationService {
         }
     }
 
+
     @Override
     public ValidationResult validateJsonFormat(final String json) {
         try {
@@ -193,6 +206,148 @@ public class DefaultValidationService implements ValidationService {
         return ValidationResult.ok();
     }
 
+    @Override
+    public ValidationResult validateData(final byte[] decryptedData, final Submission submission, final Metadata metadata, final AuthenticationTags authenticationTags) {
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#authentication-tag-pr%C3%BCfen-1
+        final var dataAuthTag = getAuthenticationTagFromEncryptedData(submission.getEncryptedData());
+        if (!authenticationTags.getData().equals(dataAuthTag)) {
+            return ValidationResult.problem(new IncorrectDataAuthenticationTag());
+        }
+
+        final Data data = metadata.getContentStructure().getData();
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#submission-data-hash
+        final String hashFromSender = data.getHash().getContent();
+        final ValidationResult hashValidation = validateHashIntegrity(hashFromSender, decryptedData);
+        if (hashValidation.hasError()) {
+            return ValidationResult.problem(new DataHashMismatch());
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#syntax-validierung-1
+        if (data.getSubmissionSchema().getMimeType().equals(MimeType.APPLICATION_JSON)) {
+            final ValidationResult jsonValidation = validateJsonFormat(new String(decryptedData, StandardCharsets.UTF_8));
+            if (jsonValidation.hasError()) {
+                return ValidationResult.problem(new DataJsonSyntaxViolation());
+            }
+        }
+
+        if (data.getSubmissionSchema().getMimeType().equals(MimeType.APPLICATION_XML)) {
+            final ValidationResult xmlValidation = validateXmlFormat(new String(decryptedData, StandardCharsets.UTF_8));
+            if (xmlValidation.hasError()) {
+                return ValidationResult.problem(new DataXmlSyntaxViolation());
+            }
+        }
+        return ValidationResult.ok();
+    }
+
+    @Override
+    public ValidationResult validateAttachments(final List<AttachmentForValidation> attachmentsForValidation, final AuthenticationTags authenticationTags) {
+
+        final Map<UUID, String> eventAuthTags = authenticationTags.getAttachments();
+        final List<Problem> validationProblems = new ArrayList<>();
+
+        for (final AttachmentForValidation attachment : attachmentsForValidation) {
+
+            final UUID attachmentId = attachment.getAttachmentId();
+
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#authentication-tag-pr%C3%BCfen-2
+            final String authTagFromEvent = eventAuthTags.get(attachmentId);
+            final String authTagFromAttachment = getAuthenticationTagFromEncryptedData(attachment.getEncryptedData());
+            if (!authTagFromEvent.equals(authTagFromAttachment)) {
+                validationProblems.add(new IncorrectAttachmentAuthenticationTag(attachmentId));
+            }
+
+            // https://docs.fitko.de/fit-connect/docs/receiving/verification/#attachment-hash
+            final ValidationResult validationResult = validateHashIntegrity(attachment.getMetadataHash(), attachment.getDecryptedData());
+            if (validationResult.hasError()) {
+                validationProblems.add(new AttachmentHashMismatch(attachmentId));
+            }
+        }
+
+        return validationProblems.isEmpty() ? ValidationResult.ok() : ValidationResult.problems(validationProblems);
+    }
+
+    @Override
+    public ValidationResult validateMetadata(final Metadata metadata, final Submission submission, final Destination destination, final AuthenticationTags eventAuthenticationTags) {
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#authentication-tag-pr%C3%BCfen
+        final var submissionMetadataAuthTag = getAuthenticationTagFromEncryptedData(submission.getEncryptedMetadata());
+        if (!eventAuthenticationTags.getMetadata().equals(submissionMetadataAuthTag)) {
+            return ValidationResult.problem(new IncorrectMetadataAuthenticationTag());
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#syntax-validierung
+        if (invalidJsonSyntax(metadata)) {
+            return ValidationResult.problem(new MetadataJsonSyntaxViolation());
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#fachdatensatz
+        if (metadata.getContentStructure().getData() == null) {
+            return ValidationResult.problem(new MissingData());
+        }
+
+        final ValidationResult validationResult = validateMetadataSchema(metadata);
+        if (validationResult.hasProblems()) {
+            return validationResult;
+        }
+
+        if (metadata.getPublicServiceType() != null) {
+            final String serviceIdentifier = metadata.getPublicServiceType().getIdentifier();
+            if (!serviceIdentifier.equals(submission.getServiceType().getIdentifier())) {
+                return ValidationResult.problem(new ServiceMismatch());
+            }
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#verwaltungsleistung-abgleichen-ii
+        final var destinationService = destination.getServices()
+                .stream()
+                .map(DestinationService::getIdentifier)
+                .filter(identifier -> identifier.equals(submission.getServiceType().getIdentifier()))
+                .findFirst();
+
+        if (destinationService.isEmpty()) {
+            return ValidationResult.problem(new UnsupportedService());
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#fachdatenschema
+        if (destination.getServices().isEmpty()) {
+            return ValidationResult.problem(new UnsupportedDataSchema());
+        }
+        final URI submissionDataSchemaUri = metadata.getContentStructure().getData().getSubmissionSchema().getSchemaUri();
+        final boolean matchingSchemas = matchingDestinationAndSubmissionSchema(destination, submissionDataSchemaUri);
+        if (!matchingSchemas) {
+            return ValidationResult.problem(new UnsupportedDataSchema());
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#liste-der-anlagen-abgleichen
+        final ValidationResult attachmentValidation = checkAttachmentCount(submission, metadata, eventAuthenticationTags);
+        if (attachmentValidation.hasProblems()) {
+            return attachmentValidation;
+        }
+
+        // https://docs.fitko.de/fit-connect/docs/receiving/verification/#r%C3%BCckkanal
+        final ReplyChannel submissionReplyChannel = metadata.getReplyChannel();
+        final ReplyChannel destinationReplyChannel = destination.getReplyChannels();
+
+        if (submissionReplyChannel != null) {
+            final List<Class<?>> submissionReplyChannelClass = getNonNullReplyChannelTypes(submissionReplyChannel);
+            final List<Class<?>> destinationReplyChannelClass = getNonNullReplyChannelTypes(destinationReplyChannel);
+            if (!new HashSet<>(destinationReplyChannelClass).containsAll(submissionReplyChannelClass)) {
+                return ValidationResult.problem(new UnsupportedReplyChannel());
+            }
+        }
+
+        return ValidationResult.ok();
+    }
+
+    private static boolean matchingDestinationAndSubmissionSchema(final Destination destination, final URI submissionDataSchemaUri) {
+        return destination.getServices().stream()
+                .flatMap(service -> service.getSubmissionSchemas().stream())
+                .map(SubmissionSchema::getSchemaUri)
+                .anyMatch(submissionDataSchemaUri::equals);
+    }
+
     private ValidationResult validate2020JsonSchema(final String schema, final JsonNode inputNode) {
         return returnValidationResult(SCHEMA_FACTORY_DRAFT_2020.getSchema(schema).validate(inputNode));
     }
@@ -209,7 +364,7 @@ public class DefaultValidationService implements ValidationService {
         if (errors.isEmpty()) {
             return ValidationResult.ok();
         }
-        return ValidationResult.error(new ValidationException(errorsToSingleString(errors)));
+        return ValidationResult.withErrorAndProblem(new ValidationException(errorsToSingleString(errors)), new MetadataSchemaViolation());
     }
 
     private JWKValidator addLogLevel(final JWKValidatorBuilder.JWKValidatorX5CErrorHandling validator) {
@@ -229,12 +384,12 @@ public class DefaultValidationService implements ValidationService {
     void validateCertChain(final RSAKey publicKey, final KeyOperation purpose) throws JWKValidationException {
 
         final List<X509Certificate> x509CertChain = publicKey.getParsedX509CertChain();
-        if(x509CertChain == null){
-            throw new IllegalStateException("public key with id '"+publicKey.getKeyID()+"' does not contain a certificate chain");
+        if (x509CertChain == null) {
+            throw new IllegalStateException("public key with id '" + publicKey.getKeyID() + "' does not contain a certificate chain");
         }
         final List<String> trustedRootCertificates = this.loadTrustedRootCertificates();
         LOGGER.info("Validation public key with XC5 certificate chain checks");
-        if (isProxySet()) {
+        if (config.isProxySet()) {
             validateWithX509AndProxy(trustedRootCertificates, publicKey, purpose);
         } else {
             validateWithX509AndWithoutProxy(trustedRootCertificates, publicKey, purpose);
@@ -289,4 +444,43 @@ public class DefaultValidationService implements ValidationService {
         return saxParserFactory.newSAXParser().getXMLReader();
     }
 
+    private static List<Class<?>> getNonNullReplyChannelTypes(final ReplyChannel destinationReplyChannel) {
+        return Stream.of(destinationReplyChannel.getEMail(),
+                        destinationReplyChannel.getDeMail(),
+                        destinationReplyChannel.getFink(),
+                        destinationReplyChannel.getElster())
+                .filter(Objects::nonNull)
+                .map(Object::getClass)
+                .collect(Collectors.toList());
+    }
+
+    private boolean invalidJsonSyntax(final Metadata metadata) {
+        try {
+            return metadata == null || validateJsonFormat(MAPPER.writeValueAsString(metadata)).hasError();
+        } catch (final JsonProcessingException | NullPointerException e) {
+            return true;
+        }
+    }
+
+    private ValidationResult checkAttachmentCount(final Submission submission, final Metadata metadata, final AuthenticationTags authenticationTags) {
+
+        final List<UUID> submissionAttachmentIds = submission.getAttachments();
+        final List<ApiAttachment> metadataAttachments = metadata.getContentStructure().getAttachments();
+        final Map<UUID, String> attachmentAuthTags = authenticationTags.getAttachments();
+
+        if ((isNull(submissionAttachmentIds) || submissionAttachmentIds.isEmpty()) && (isNull(attachmentAuthTags) || attachmentAuthTags.isEmpty())) {
+            return ValidationResult.ok();
+        }
+
+        if (metadataAttachments.size() != submissionAttachmentIds.size()
+                || attachmentAuthTags.size() != submissionAttachmentIds.size()
+                || !attachmentAuthTags.keySet().containsAll(submissionAttachmentIds)
+                || !submissionAttachmentIds.stream().allMatch(id -> metadataAttachments.stream().anyMatch(m -> m.getAttachmentId().equals(id)))
+                || !submissionAttachmentIds.stream().allMatch(attachmentAuthTags::containsKey)) {
+            return ValidationResult.problem(new AttachmentsMismatch());
+        }
+
+        return ValidationResult.ok();
+    }
+
 }
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
index bc2c94a5011bde267e1792813ab0e818febe77a1..88ee81c44b3cddc395b3325f31adae0bcacb26ce 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
@@ -83,25 +83,6 @@ public class SubmissionSenderTest {
         );
     }
 
-    @Test
-    void validatePublicKey() throws JOSEException {
-
-        //Given
-        final RSAKey publicKey = new RSAKeyGenerator(2048)
-                .keyUse(KeyUse.ENCRYPTION)
-                .keyID(UUID.randomUUID().toString())
-                .generate()
-                .toPublicJWK();
-
-        when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.ok());
-
-        // When
-        final ValidationResult validationResult = underTest.validatePublicKey(publicKey);
-
-        // Then
-        assertTrue(validationResult.isValid());
-    }
-
     @Test
     void encryptBytes() throws JOSEException {
 
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
index 4f1cec09d6cf6df36b3d29b3f441d9bd0db33e6d..08813b25f7a59a1d805dccdaf31f56828c0c8506 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
@@ -7,8 +7,14 @@ import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
 import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.MetadataSchemaViolation;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.AttachmentsMismatch;
+import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionsForPickup;
@@ -33,6 +39,7 @@ import java.util.UUID;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -40,6 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -167,10 +175,10 @@ class SubmissionSubscriberTest {
     void validateMetadata() {
 
         // Given
-        when(validationServiceMock.validateMetadataSchema(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validateMetadata(any(), any(), any(), any())).thenReturn(ValidationResult.ok());
 
         // When
-        final ValidationResult validationResult = underTest.validateMetadata(new Metadata());
+        final ValidationResult validationResult = underTest.validateMetadata(new Metadata(), new Submission(), new AuthenticationTags());
 
         // Then
         assertTrue(validationResult.isValid());
@@ -180,14 +188,28 @@ class SubmissionSubscriberTest {
     void invalidMetadata() {
 
         // Given
-        when(validationServiceMock.validateMetadataSchema(any())).thenReturn(ValidationResult.error(new Exception("Failed parsing metadata")));
+        when(validationServiceMock.validateMetadata(any(), any(), any(), any())).thenReturn(ValidationResult.problem(new MetadataSchemaViolation()));
 
         // When
-        final ValidationResult validationResult = underTest.validateMetadata(new Metadata());
+        final ValidationResult validationResult = underTest.validateMetadata(new Metadata(), new Submission(), new AuthenticationTags());
 
         // Then
-        assertTrue(validationResult.hasError());
-        assertThat(validationResult.getError().getMessage(), containsString("Failed parsing metadata"));
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(MetadataSchemaViolation.class));
+    }
+
+    @Test
+    void invalidData() {
+
+        // Given
+        when(validationServiceMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.problem(new DataHashMismatch()));
+
+        // When
+        final ValidationResult validationResult = underTest.validateData("foo".getBytes(), new Submission(), new Metadata(), new AuthenticationTags());
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(DataHashMismatch.class));
     }
 
     @Test
@@ -300,6 +322,29 @@ class SubmissionSubscriberTest {
         assertThrows(EventLogException.class, () -> underTest.getEventLog(destinationId, caseId));
     }
 
+    @Test
+    void testAttachmentValidationFailed() {
+
+        // Given
+        final Hash hash = new Hash();
+        hash.setContent("hashValue");
+
+        final ApiAttachment attachmentMetadata = new ApiAttachment();
+        attachmentMetadata.setAttachmentId(UUID.randomUUID());
+        attachmentMetadata.setHash(hash);
+
+        final AttachmentForValidation attachmentForValidation = new AttachmentForValidation(attachmentMetadata, null, null);
+
+        when(validationServiceMock.validateAttachments(anyList(), any())).thenReturn(ValidationResult.problem(new AttachmentsMismatch()));
+
+        // When
+        final ValidationResult validationResult = underTest.validateAttachments(List.of(attachmentForValidation), new AuthenticationTags());
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(AttachmentsMismatch.class));
+    }
+
 
     private String getResourceAsString(final String filename) throws IOException {
         return new String(SubmissionSenderTest.class.getResourceAsStream(filename).readAllBytes());
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
index 4a3231e07256d377aea1428d6eac17c7a84f2bb9..5ffbed66bd10d45e100021a38cca55da4d91ff51 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
@@ -75,7 +75,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -113,7 +113,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId, authenticationTags);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -142,7 +142,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId, null);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -178,7 +178,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId, authTags);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -216,7 +216,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -254,7 +254,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.error(new SchemaNotFoundException("Schema not found")));
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -289,7 +289,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -330,7 +330,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -371,7 +371,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -410,7 +410,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -450,7 +450,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getSubmissionServicePublicKey(any())).thenReturn(rsaKey);
 
         // When
@@ -488,7 +488,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -526,7 +526,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -563,7 +563,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -602,7 +602,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(rsaKey);
 
         // When
@@ -639,7 +639,7 @@ class EventLogVerifierTest {
         final ValidationContext ctx = new ValidationContext(destinationId, caseId);
 
         when(validationServiceMock.validateSetEventSchema(any())).thenReturn(ValidationResult.ok());
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
         when(keyServiceMock.getPublicSignatureKey(any(), any())).thenReturn(null);
 
         // When
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/ValidationContextTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/ValidationContextTest.java
index 8d8e02bd07fe80da53d8cb7c9c2976015ec2e292..4ff9591ebf96533d8efba98dd3081ca1f7929a6c 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/ValidationContextTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/ValidationContextTest.java
@@ -23,7 +23,7 @@ class ValidationContextTest {
         final ValidationContext underTest = new ValidationContext(UUID.randomUUID(), UUID.randomUUID());
 
         // When
-        underTest.addResult(1+2 == 3, "1+2 does not equal 3");
+        underTest.addErrorIfTestFailed(1+2 == 3, "1+2 does not equal 3");
         final List<ValidationResult> results = underTest.getValidationResults();
 
         // Then
@@ -37,7 +37,7 @@ class ValidationContextTest {
         final ValidationContext underTest = new ValidationContext(UUID.randomUUID(), UUID.randomUUID());
 
         // When
-        underTest.addResult(1+2 == 2, "1+2 does not equal 3");
+        underTest.addErrorIfTestFailed(1+2 == 2, "1+2 does not equal 3");
         final List<ValidationResult> results = underTest.getValidationResults();
 
         // Then
@@ -71,7 +71,7 @@ class ValidationContextTest {
         final List<ValidationResult> results = underTest.getValidationResults();
 
         // Then
-        assertTrue(results.size() == 1);
+        assertEquals(1, results.size());
         assertTrue(results.get(0).hasError());
         assertThat(results.get(0).getError().getMessage(), is("Test failed"));
     }
@@ -87,7 +87,7 @@ class ValidationContextTest {
         final List<ValidationResult> results = underTest.getValidationResults();
 
         // Then
-        assertTrue(results.size() == 1);
+        assertEquals(1, results.size());
         assertTrue(results.get(0).hasError());
         assertThat(results.get(0).getError().getMessage(), is("Test failed"));
     }
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
index 54b90334da0c9a89fc1ae3323418afa358daa032..82e637c367d39bdc0adf42987fadb1878dcc3429 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
@@ -91,7 +91,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
         when(submissionServiceMock.getDestination(any())).thenReturn(destination);
-        when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final RSAKey rsaKey = underTest.getPublicEncryptionKey(destination.getDestinationId());
@@ -134,7 +134,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
         when(submissionServiceMock.getDestination(any())).thenReturn(destination);
-        when(validationServiceMock.validateEncryptionPublicKey(any())).thenReturn(ValidationResult.error(new Exception("Public key is insecure !")));
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.error(new Exception("Public key is insecure !")));
 
         // When
         final InvalidKeyException exception = assertThrows(InvalidKeyException.class, () -> keyService.getPublicEncryptionKey(destination.getDestinationId()));
@@ -165,7 +165,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
         when(submissionServiceMock.getDestination(any())).thenReturn(destination);
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final RSAKey retrievedSignatureKey = underTest.getPublicSignatureKey(destination.getDestinationId(), "123");
@@ -195,7 +195,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
                                 .withStatus(200)));
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final RSAKey retrievedSignatureKey = underTest.getSubmissionServicePublicKey("123");
@@ -225,7 +225,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
                                 .withStatus(200)));
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final RSAKey retrievedSignatureKey = underTest.getPortalPublicKey("123");
@@ -255,7 +255,7 @@ class PublicKeyServiceTest extends RestEndpointBase {
                                 .withStatus(200)));
 
         when(authServiceMock.getCurrentToken()).thenReturn(authToken);
-        when(validationServiceMock.validateSignaturePublicKey(any())).thenReturn(ValidationResult.ok());
+        when(validationServiceMock.validatePublicKey(any(), any())).thenReturn(ValidationResult.ok());
 
         // When
         final RSAKey retrievedSignatureKey = underTest.getWellKnownKeysForSubmissionUrl("http://localhost:" + wireMockServer.port() + "/custom/path", "123");
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java b/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
index b2b4367b2b31f5b389432bfb5efadd69c6a7ac86..4ba6454ce2cc4e9b9fa5527b4f2fcb2037743d3a 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
@@ -5,6 +5,7 @@ import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
 import dev.fitko.fitconnect.api.domain.model.event.EventLog;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
 import dev.fitko.fitconnect.api.exceptions.EventLogException;
 import org.junit.jupiter.api.Test;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
index 72cadba47b33ae64b7030be930c97a90fc42fb33..877380dce434e0c33e6e842661d05835f1ef5dde 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
@@ -12,16 +12,30 @@ import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.config.Environment;
 import dev.fitko.fitconnect.api.config.EnvironmentName;
 import dev.fitko.fitconnect.api.config.SchemaConfig;
-import dev.fitko.fitconnect.api.domain.model.metadata.ContentStructure;
-import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
-import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
-import dev.fitko.fitconnect.api.domain.model.metadata.SignatureType;
+import dev.fitko.fitconnect.api.domain.model.destination.Destination;
+import dev.fitko.fitconnect.api.domain.model.destination.DestinationService;
+import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
+import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.AttachmentHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.IncorrectAttachmentAuthenticationTag;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataJsonSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataXmlSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAuthenticationTag;
+import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.*;
+import dev.fitko.fitconnect.api.domain.model.metadata.*;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
+import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
+import dev.fitko.fitconnect.api.domain.model.replychannel.Email;
+import dev.fitko.fitconnect.api.domain.model.replychannel.Fink;
+import dev.fitko.fitconnect.api.domain.model.replychannel.ReplyChannel;
+import dev.fitko.fitconnect.api.domain.model.submission.ServiceType;
+import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
 import dev.fitko.fitconnect.api.services.crypto.MessageDigestService;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import dev.fitko.fitconnect.core.crypto.HashService;
@@ -39,10 +53,11 @@ import java.net.URI;
 import java.text.ParseException;
 import java.time.ZonedDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.*;
-import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.*;
 
@@ -56,11 +71,13 @@ class DefaultValidationServiceTest {
     @BeforeEach
     void setUp() {
         final var config = getApplicationConfig(true);
-        hashService = new HashService();
+
         final List<String> setSchemas = SchemaConfig.getSetSchemaFilePaths("/set-schema");
         final List<String> metadataSchemas = SchemaConfig.getMetadataSchemaFileNames("/metadata-schema");
         final List<String> destinationSchemas = SchemaConfig.getDestinationSchemaPaths("/destination-schema");
         final SchemaResources schemaResources = new SchemaResources(setSchemas, metadataSchemas, destinationSchemas);
+
+        hashService = new HashService();
         schemaProvider = new SchemaResourceProvider(schemaResources);
         underTest = new DefaultValidationService(config, hashService, schemaProvider,
                 FileUtil.loadContentOfFilesInDirectory("trusted-test-root-certificates"));
@@ -77,7 +94,7 @@ class DefaultValidationServiceTest {
                 .generate();
 
         // When
-        final ValidationResult validationResult = underTest.validateEncryptionPublicKey(rsaKey);
+        final ValidationResult validationResult = underTest.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
 
         // Then
         assertTrue(validationResult.isValid());
@@ -94,7 +111,7 @@ class DefaultValidationServiceTest {
                 .generate();
 
         // When
-        final ValidationResult validationResult = underTest.validateEncryptionPublicKey(rsaKey);
+        final ValidationResult validationResult = underTest.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
 
         // Then
         assertTrue(validationResult.hasError());
@@ -122,7 +139,7 @@ class DefaultValidationServiceTest {
         final RSAKey rsaKey = getRsaKeyWithCertChain();
 
         // When
-        underTest.validateEncryptionPublicKey(rsaKey);
+        underTest.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
 
         // Then
         logs.assertContains("Using proxy HTTP @ https://localhost:8080 for key validation");
@@ -143,7 +160,7 @@ class DefaultValidationServiceTest {
                 .generate();
 
         // When
-        final ValidationResult validationResult = underTest.validateEncryptionPublicKey(rsaKey);
+        final ValidationResult validationResult = underTest.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
 
         // Then
         assertTrue(validationResult.isValid());
@@ -164,7 +181,7 @@ class DefaultValidationServiceTest {
                 .generate();
 
         // When
-        final ValidationResult validationResult = underTest.validateEncryptionPublicKey(rsaKey);
+        final ValidationResult validationResult = underTest.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
 
         // Then
         assertTrue(validationResult.hasError());
@@ -173,7 +190,7 @@ class DefaultValidationServiceTest {
 
 
     @Test
-    void validateMetadata() {
+    void testValidMetadataSchema() {
 
         // Given
         final var submissionSchema = new SubmissionSchema();
@@ -203,6 +220,474 @@ class DefaultValidationServiceTest {
         assertTrue(validationResult.isValid());
     }
 
+    @Test
+    void testValidMetadata() {
+
+        // Given
+        final var submissionSchema = new SubmissionSchema();
+        submissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json"));
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var data = new Data();
+        data.setSubmissionSchema(submissionSchema);
+        data.setHash(hash);
+
+        final var attachmentOne = new ApiAttachment();
+        attachmentOne.setAttachmentId(UUID.randomUUID());
+
+        final var attachmentTwo = new ApiAttachment();
+        attachmentTwo.setAttachmentId(UUID.randomUUID());
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+        contentStructure.setAttachments(List.of(attachmentOne, attachmentTwo));
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final ReplyChannel replyChannel = new ReplyChannel();
+        replyChannel.setEMail(new Email("test@mail.net", false, null));
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+        metadata.setReplyChannel(replyChannel);
+
+        final ServiceType serviceType = new ServiceType();
+        serviceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(serviceType);
+        submission.setAttachments(contentStructure.getAttachments().stream().map(ApiAttachment::getAttachmentId).collect(Collectors.toList()));
+
+        final DestinationService destinationService = new DestinationService();
+        destinationService.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+        destinationService.setSubmissionSchemas(Set.of(submissionSchema));
+
+        final var destination = new Destination();
+        destination.setServices(Set.of(destinationService));
+        destination.setReplyChannels(replyChannel);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+        authenticationTags.setAttachments(Map.of(attachmentOne.getAttachmentId(), "authTag", attachmentTwo.getAttachmentId(), "authTag"));
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.isValid());
+    }
+
+    @Test
+    void testIncorrectMetadataAuthenticationTag() {
+
+        // Given
+        final var metadata = new Metadata();
+        final var destination = new Destination();
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("wrongMetadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), is(new IncorrectMetadataAuthenticationTag()));
+    }
+
+
+    @Test
+    void testUnsupportedMetadataSchema() {
+
+        // Given
+        final String unsupportedSchema = "https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json/metadata_schema_3.2.1.json";
+
+        final Data data = new Data();
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+        metadata.setSchema(unsupportedSchema);
+        final var destination = new Destination();
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), is(new UnsupportedMetadataSchema(unsupportedSchema)));
+    }
+
+    @Test
+    void testMetadataSyntaxViolation() {
+
+        // Given
+        final var destination = new Destination();
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(null, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(MetadataJsonSyntaxViolation.class));
+    }
+
+    @Test
+    void testMissingData() {
+
+        // Given
+        final var destination = new Destination();
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(new ContentStructure());
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(MissingData.class));
+    }
+
+    @Test
+    void testMetadataSchemaViolation() {
+
+        // Given
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setData(new Data());
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+
+        final var destination = new Destination();
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), is(new MetadataSchemaViolation()));
+    }
+
+    @Test
+    void testServiceMismatch() {
+
+        // Given
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final SubmissionSchema submissionSchema = new SubmissionSchema();
+        submissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"));
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final var data = new Data();
+        data.setSubmissionSchema(submissionSchema);
+        data.setHash(hash);
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+        contentStructure.setAttachments(Collections.emptyList());
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:123456789");
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+
+        final var destination = new Destination();
+
+        final ServiceType submissionServiceType = new ServiceType();
+        submissionServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(submissionServiceType);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(ServiceMismatch.class));
+    }
+
+    @Test
+    void testUnsupportedService() {
+
+        // Given
+        final SubmissionSchema submissionSchema = new SubmissionSchema();
+        submissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"));
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Hash hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(submissionSchema);
+        data.setHash(hash);
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setAttachments(Collections.emptyList());
+        contentStructure.setData(data);
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+
+        final DestinationService destinationService = new DestinationService();
+        destinationService.setIdentifier("urn:de:fim:leika:leistung:9999999999999");
+
+        final var destination = new Destination();
+        destination.setServices(Set.of(destinationService));
+
+        final ServiceType submissionServiceType = new ServiceType();
+        submissionServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(submissionServiceType);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(UnsupportedService.class));
+    }
+
+    @Test
+    void testUnsupportedDataSchema() {
+
+        // Given
+        final SubmissionSchema submissionSchema = new SubmissionSchema();
+        submissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"));
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Hash hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(submissionSchema);
+        data.setHash(hash);
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setAttachments(Collections.emptyList());
+        contentStructure.setData(data);
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+
+        final SubmissionSchema destinationSubmissionSchema = new SubmissionSchema();
+        destinationSubmissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/3.0.0/metadata.schema.json"));
+        destinationSubmissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final DestinationService destinationService = new DestinationService();
+        destinationService.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+        destinationService.setSubmissionSchemas(Set.of(destinationSubmissionSchema));
+
+        final var destination = new Destination();
+        destination.setServices(Set.of(destinationService));
+
+        final ServiceType submissionServiceType = new ServiceType();
+        submissionServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(submissionServiceType);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(UnsupportedDataSchema.class));
+    }
+
+    @Test
+    void testAttachmentsMismatch() {
+
+        // Given
+        final SubmissionSchema schema = new SubmissionSchema();
+        schema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"));
+        schema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Hash hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(schema);
+        data.setHash(hash);
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+
+        final DestinationService destinationService = new DestinationService();
+        destinationService.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+        destinationService.setSubmissionSchemas(Set.of(schema));
+
+        final var destination = new Destination();
+        destination.setServices(Set.of(destinationService));
+
+        final ServiceType submissionServiceType = new ServiceType();
+        submissionServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(submissionServiceType);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        // attachmentId that were announced
+        submission.setAttachments(List.of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()));
+
+        // attachment in metadata that doesn't match
+        final ApiAttachment attachment = new ApiAttachment();
+        attachment.setAttachmentId(UUID.randomUUID());
+        contentStructure.setAttachments(List.of(attachment));
+
+        // authTag from submit event
+        authenticationTags.setAttachments(Map.of(attachment.getAttachmentId(), "attachmentAuthTag"));
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(AttachmentsMismatch.class));
+    }
+
+    @Test
+    void testUnsupportedReplyChannel() {
+
+        // Given
+        final SubmissionSchema schema = new SubmissionSchema();
+        schema.setSchemaUri(URI.create("https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"));
+        schema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final Hash hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("someBogusContent".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final Data data = new Data();
+        data.setSubmissionSchema(schema);
+        data.setHash(hash);
+
+        final ContentStructure contentStructure = new ContentStructure();
+        contentStructure.setAttachments(Collections.emptyList());
+        contentStructure.setData(data);
+
+        final PublicServiceType publicServiceType = new PublicServiceType();
+        publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var metadata = new Metadata();
+        metadata.setSchema(SchemaConfig.METADATA_V_1_0_0.toString());
+        metadata.setContentStructure(contentStructure);
+        metadata.setPublicServiceType(publicServiceType);
+
+        final DestinationService destinationService = new DestinationService();
+        destinationService.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+        destinationService.setSubmissionSchemas(Set.of(schema));
+
+        final var destination = new Destination();
+        destination.setServices(Set.of(destinationService));
+
+        final ServiceType submissionServiceType = new ServiceType();
+        submissionServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
+
+        final var submission = new Submission();
+        submission.setEncryptedMetadata("header.encryption_key.init_vector.ciphertext.metadataAuthTag");
+        submission.setServiceType(submissionServiceType);
+
+        final var authenticationTags = new AuthenticationTags();
+        authenticationTags.setMetadata("metadataAuthTag");
+
+        final ReplyChannel submissionReplyChannel = new ReplyChannel();
+        submissionReplyChannel.setEMail(new Email("test@mail.org", false, null));
+        metadata.setReplyChannel(submissionReplyChannel);
+
+        final ReplyChannel destinationReplyChannel = new ReplyChannel();
+        destinationReplyChannel.setFink(new Fink("finkPostBoxRef", "finkHost"));
+        destination.setReplyChannels(destinationReplyChannel);
+
+        // When
+        final ValidationResult validationResult = underTest.validateMetadata(metadata, submission, destination, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(UnsupportedReplyChannel.class));
+    }
+
     @Test
     void validateMetadataWithoutSchemaAttributeButValidSchema() {
 
@@ -223,9 +708,8 @@ class DefaultValidationServiceTest {
 
         final ValidationResult validationResult = underTest.validateMetadataSchema(metadata);
 
-        assertTrue(validationResult.hasError());
-        assertThat(validationResult.getError().getClass(), equalTo(ValidationException.class));
-        assertThat(validationResult.getError().getMessage(), containsString("The provided metadata schema is not supported."));
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems(), contains(new UnsupportedMetadataSchema(metadata.getSchema())));
     }
 
     @Test
@@ -287,6 +771,276 @@ class DefaultValidationServiceTest {
         assertThat(validationResult.getError().getMessage(), containsString("$.contentStructure.data.hash.content: does not match the regex pattern ^[a-f0-9]{128}$"));
     }
 
+    @Test
+    void testIncorrectAttachmentAuthenticationTag() {
+
+        // Given
+        final var firstAttachmentId = UUID.randomUUID();
+        final var secondAttachmentId = UUID.randomUUID();
+
+        final var firstDecryptedData = "first test attachment".getBytes();
+        final var firstEncryptedData = "part1.part2.part3.part4.authTag1";
+        final var firstOriginalHash = hashService.toHexString(hashService.createHash(firstDecryptedData));
+
+        final byte[] secondDecryptedData = "second test attachment".getBytes();
+        final var secondEncryptedData = "part1.part2.part3.part4.authTag2";
+        final var secondOriginalHash = hashService.toHexString(hashService.createHash(secondDecryptedData));
+
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setAttachments(Map.of(firstAttachmentId, "notMatchingAuthTag1", secondAttachmentId, "notMatchingAuthTag2"));
+
+        final Hash firstHash = new Hash();
+        firstHash.setContent(firstOriginalHash);
+
+        final ApiAttachment firstAttachmentMetadata = new ApiAttachment();
+        firstAttachmentMetadata.setAttachmentId(firstAttachmentId);
+        firstAttachmentMetadata.setHash(firstHash);
+
+        final Hash secondHash = new Hash();
+        secondHash.setContent(secondOriginalHash);
+
+        final ApiAttachment secondAttachmentMetadata = new ApiAttachment();
+        secondAttachmentMetadata.setAttachmentId(secondAttachmentId);
+        secondAttachmentMetadata.setHash(secondHash);
+
+        final AttachmentForValidation firstAttachment = new AttachmentForValidation(firstAttachmentMetadata, firstEncryptedData, firstDecryptedData);
+        final AttachmentForValidation secondAttachment = new AttachmentForValidation(secondAttachmentMetadata, secondEncryptedData, secondDecryptedData);
+
+        // When
+        final ValidationResult validationResult = underTest.validateAttachments(List.of(firstAttachment, secondAttachment), authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems(), hasSize(2));
+        assertThat(validationResult.getProblems().stream().map(Problem::getClass).collect(Collectors.toList()), hasItems(IncorrectAttachmentAuthenticationTag.class));
+        assertThat(validationResult.getProblems().get(0).getInstance(), containsString("attachment:" + firstAttachmentId));
+        assertThat(validationResult.getProblems().get(1).getInstance(), containsString("attachment:" + secondAttachmentId));
+    }
+
+    @Test
+    void testAttachmentHashMismatch() {
+
+        // Given
+        final var firstAttachmentId = UUID.randomUUID();
+        final var secondAttachmentId = UUID.randomUUID();
+
+        final var firstDecryptedData = "first test attachment".getBytes();
+        final var firstEncryptedData = "part1.part2.part3.part4.authTag1";
+        final var firstOriginalHash = hashService.toHexString(hashService.createHash(firstDecryptedData));
+
+        final byte[] secondDecryptedData = "second test attachment".getBytes();
+        final var secondEncryptedData = "part1.part2.part3.part4.authTag2";
+        final var secondOriginalHash = hashService.toHexString(hashService.createHash(secondDecryptedData));
+
+        final AuthenticationTags authenticationTags = new AuthenticationTags();
+        authenticationTags.setAttachments(Map.of(firstAttachmentId, "authTag1", secondAttachmentId, "authTag2"));
+
+        final Hash firstHash = new Hash();
+        firstHash.setContent(firstOriginalHash);
+
+        final ApiAttachment firstAttachmentMetadata = new ApiAttachment();
+        firstAttachmentMetadata.setAttachmentId(firstAttachmentId);
+        firstAttachmentMetadata.setHash(firstHash);
+
+        final Hash secondHash = new Hash();
+        secondHash.setContent(secondOriginalHash);
+
+        final ApiAttachment secondAttachmentMetadata = new ApiAttachment();
+        secondAttachmentMetadata.setAttachmentId(secondAttachmentId);
+        secondAttachmentMetadata.setHash(secondHash);
+
+        final AttachmentForValidation firstAttachment = new AttachmentForValidation(firstAttachmentMetadata, firstEncryptedData, "modified attachment 1 content".getBytes());
+        final AttachmentForValidation secondAttachment = new AttachmentForValidation(secondAttachmentMetadata, secondEncryptedData, "modified attachment 2 content".getBytes());
+
+        // When
+        final ValidationResult validationResult = underTest.validateAttachments(List.of(firstAttachment, secondAttachment), authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems(), hasSize(2));
+        assertThat(validationResult.getProblems().stream().map(Problem::getClass).collect(Collectors.toList()), hasItems(AttachmentHashMismatch.class));
+        assertThat(validationResult.getProblems().get(0).getInstance(), containsString("attachment:" + firstAttachmentId));
+        assertThat(validationResult.getProblems().get(1).getInstance(), containsString("attachment:" + secondAttachmentId));
+    }
+
+    @Test
+    void testValidData() {
+
+        // Given
+        final var decryptedData = "{ \"fachdaten\" : \"test\"}".getBytes();
+
+        final var submission = new Submission();
+        submission.setEncryptedData("part1.part2.part3.part4.dataAuthTag");
+
+        final var submissionSchema = new SubmissionSchema();
+        submissionSchema.setSchemaUri(URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json"));
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash(decryptedData)));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var data = new Data();
+        data.setHash(hash);
+        data.setSubmissionSchema(submissionSchema);
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+        contentStructure.setAttachments(Collections.emptyList());
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final var authenticationTags =new AuthenticationTags();
+        authenticationTags.setData("dataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateData(decryptedData, submission, metadata, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.isValid());
+    }
+
+    @Test
+    void testIncorrectDataAuthenticationTag() {
+
+        // Given
+        final var decryptedData = "{ \"fachdaten\" : \"test\"}".getBytes();
+
+        final var submission = new Submission();
+        submission.setEncryptedData("part1.part2.part3.part4.wrongDataAuthTag");
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash(decryptedData)));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var data = new Data();
+        data.setHash(hash);
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final var authenticationTags =new AuthenticationTags();
+        authenticationTags.setData("dataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateData(decryptedData, submission, metadata, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(IncorrectDataAuthenticationTag.class));
+    }
+
+    @Test
+    void testDataHashMismatch() {
+
+        // Given
+        final var decryptedData = "{ \"fachdaten\" : \"test\"}".getBytes();
+
+        final var submission = new Submission();
+        submission.setEncryptedData("part1.part2.part3.part4.dataAuthTag");
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash("manipulated data".getBytes())));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var data = new Data();
+        data.setHash(hash);
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final var authenticationTags =new AuthenticationTags();
+        authenticationTags.setData("dataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateData(decryptedData, submission, metadata, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(DataHashMismatch.class));
+    }
+
+    @Test
+    void testDataJsonSyntaxViolation() {
+
+        // Given
+        final var decryptedData = "{ invalid_json }".getBytes();
+
+        final var submission = new Submission();
+        submission.setEncryptedData("part1.part2.part3.part4.dataAuthTag");
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash(decryptedData)));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var submissionSchema = new SubmissionSchema();
+        submissionSchema.setMimeType(MimeType.APPLICATION_JSON);
+
+        final var data = new Data();
+        data.setHash(hash);
+        data.setSubmissionSchema(submissionSchema);
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final var authenticationTags =new AuthenticationTags();
+        authenticationTags.setData("dataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateData(decryptedData, submission, metadata, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(DataJsonSyntaxViolation.class));
+    }
+
+    @Test
+    void testDataXmlSyntaxViolation() {
+
+        // Given
+        final var decryptedData = "<xmehl>invalid xml</xml> }".getBytes();
+
+        final var submission = new Submission();
+        submission.setEncryptedData("part1.part2.part3.part4.dataAuthTag");
+
+        final var hash = new Hash();
+        hash.setContent(hashService.toHexString(hashService.createHash(decryptedData)));
+        hash.setSignatureType(SignatureType.SHA_512);
+
+        final var submissionSchema = new SubmissionSchema();
+        submissionSchema.setMimeType(MimeType.APPLICATION_XML);
+
+        final var data = new Data();
+        data.setHash(hash);
+        data.setSubmissionSchema(submissionSchema);
+
+        final var contentStructure = new ContentStructure();
+        contentStructure.setData(data);
+
+        final var metadata = new Metadata();
+        metadata.setContentStructure(contentStructure);
+
+        final var authenticationTags =new AuthenticationTags();
+        authenticationTags.setData("dataAuthTag");
+
+        // When
+        final ValidationResult validationResult = underTest.validateData(decryptedData, submission, metadata, authenticationTags);
+
+        // Then
+        assertTrue(validationResult.hasProblems());
+        assertThat(validationResult.getProblems().get(0), instanceOf(DataXmlSyntaxViolation.class));
+    }
+
     @Test
     void validateMatchingHash() {
 
@@ -311,7 +1065,7 @@ class DefaultValidationServiceTest {
 
         // Then
         assertTrue(validationResult.hasError());
-        assertThat(validationResult.getError().getMessage(), containsString("transmitted data does not equal the hash of the sender"));
+        assertThat(validationResult.getError().getMessage(), containsString("Metadata contains invalid hash value"));
     }
 
     @Test
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/EventLogIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/EventLogIT.java
index 303b0fb9f712892245618ae69dd90722393f0061..299736cb0d46579ca952fbb98f7f37f59f9c4fc4 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/EventLogIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/EventLogIT.java
@@ -5,15 +5,20 @@ import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionState;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAuthenticationTag;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
 import dev.fitko.fitconnect.client.SenderClient;
+import dev.fitko.fitconnect.client.SubscriberClient;
 import dev.fitko.fitconnect.client.factory.ClientFactory;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
 import dev.fitko.fitconnect.integrationtests.condition.EnableIfEnvironmentVariablesAreSet;
 import org.awaitility.Awaitility;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.nio.file.Path;
@@ -25,20 +30,30 @@ import java.util.stream.Collectors;
 
 import static org.awaitility.Awaitility.await;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.collection.IsEmptyCollection.empty;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @EnableIfEnvironmentVariablesAreSet
 public class EventLogIT extends IntegrationTestBase {
 
+    @BeforeEach
+    public void cleanup(){
+        cleanupTestSubmissions();
+    }
+
     @Test
     void testRejectEvent() {
 
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
+        final SenderClient senderClient = ClientFactory.getSenderClient(config);
+
         final var submission = SendableSubmission.Builder()
                 .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                 .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
@@ -46,9 +61,9 @@ public class EventLogIT extends IntegrationTestBase {
                 .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                 .build();
 
-        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+        final var sentSubmission = senderClient.send(submission);
 
-        Assertions.assertNotNull(sentSubmission);
+        assertNotNull(sentSubmission);
 
         // When
         final var subscriberClient = ClientFactory.getSubscriberClient(config);
@@ -56,11 +71,16 @@ public class EventLogIT extends IntegrationTestBase {
         final var sentSubmissionId = sentSubmission.getSubmissionId();
 
         // reject and remove
-        subscriberClient.requestSubmission(sentSubmissionId).rejectSubmission(List.of(new InvalidEventLog()));
+        subscriberClient.requestSubmission(sentSubmissionId).rejectSubmission(List.of(new DataEncryptionIssue()));
+
+        // check event log if reject event was sent
+        final SubmissionStatus status = senderClient.getStatusForSubmission(sentSubmission);
+        assertThat(status.getStatus(), is(SubmissionState.REJECTED));
 
         // second attempt to receive and reject the submission should return an empty result
         // since the submission is gone after being rejected
-        Assertions.assertNull(subscriberClient.requestSubmission(sentSubmissionId));
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        assertThat(exception.getMessage(), containsString("Submission not found"));
     }
 
     @Test
@@ -69,6 +89,9 @@ public class EventLogIT extends IntegrationTestBase {
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
+        final SenderClient senderClient = ClientFactory.getSenderClient(config);
+        final var subscriberClient = ClientFactory.getSubscriberClient(config);
+
         final var submission = SendableSubmission.Builder()
                 .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                 .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
@@ -76,21 +99,65 @@ public class EventLogIT extends IntegrationTestBase {
                 .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                 .build();
 
-        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+        final var sentSubmission = senderClient.send(submission);
 
         Assertions.assertNotNull(sentSubmission);
 
         // When
-        final var subscriberClient = ClientFactory.getSubscriberClient(config);
 
         final var sentSubmissionId = sentSubmission.getSubmissionId();
 
         // accept and remove
         subscriberClient.requestSubmission(sentSubmissionId).acceptSubmission();
 
+        // check event log if accept event was sent
+        final SubmissionStatus status = senderClient.getStatusForSubmission(sentSubmission);
+        assertThat(status.getStatus(), is(SubmissionState.ACCEPTED));
+
         // second attempt to receive the submission should return an empty result
         // since the submission is gone after being accepted
-        Assertions.assertNull(subscriberClient.requestSubmission(sentSubmissionId));
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        assertThat(exception.getMessage(), containsString("Submission not found"));
+    }
+
+    @Test
+    void testAcceptEventWithProblem() {
+
+        // Given
+        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
+
+        final var submission = SendableSubmission.Builder()
+                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
+                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
+                .setJsonData("{ \"data\": \"Beispiel Fachdaten\" }")
+                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
+                .build();
+
+        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+
+        assertNotNull(sentSubmission);
+
+        // When
+        final var subscriberClient = ClientFactory.getSubscriberClient(config);
+
+        final var sentSubmissionId = sentSubmission.getSubmissionId();
+
+        // accept and remove
+        subscriberClient.requestSubmission(sentSubmissionId).acceptSubmission(new IncorrectDataAuthenticationTag());
+
+        // check event log if accept event was sent and contains a problem
+        final Optional<EventLogEntry> acceptEvent = subscriberClient
+                .getEventLog(sentSubmission.getCaseId(), sentSubmission.getDestinationId())
+                .stream().filter(e -> e.getEvent().equals(Event.ACCEPT))
+                .findFirst();
+
+        assertTrue(acceptEvent.isPresent());
+        assertThat(acceptEvent.get().getProblems(), is(not(empty())));
+
+        // second attempt to receive the submission should return an empty result
+        // since the submission is gone after being accepted
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        assertThat(exception.getMessage(), containsString("Submission not found"));
     }
 
     @Test
@@ -127,8 +194,10 @@ public class EventLogIT extends IntegrationTestBase {
 
         // Then
         assertThat(senderClient.getStatusForSubmission(sentSubmission).getStatus(), is(SubmissionState.REJECTED));
+
         // second attempt to receive and reject the submission should return an empty result since the submission is gone after being rejected
-        assertNull(subscriberClient.requestSubmission(sentSubmission.getSubmissionId()));
+        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmission.getSubmissionId()));
+        assertThat(exception.getMessage(), containsString("Submission not found"));
     }
 
     @Test
@@ -136,6 +205,7 @@ public class EventLogIT extends IntegrationTestBase {
 
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
+        final SenderClient senderClient = ClientFactory.getSenderClient(config);
 
         final var submission = SendableSubmission.Builder()
                 .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
@@ -143,7 +213,7 @@ public class EventLogIT extends IntegrationTestBase {
                 .setJsonData("{ \"data\": \"Beispiel Fachdaten\" }")
                 .build();
 
-        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+        final var sentSubmission = senderClient.send(submission);
 
         Assertions.assertNotNull(sentSubmission);
 
@@ -153,7 +223,7 @@ public class EventLogIT extends IntegrationTestBase {
             final UUID destinationId = sentSubmission.getDestinationId();
             final UUID caseId = sentSubmission.getCaseId();
 
-            final List<EventLogEntry> senderEventLog = ClientFactory.getSenderClient(config).getEventLog(caseId, destinationId);
+            final List<EventLogEntry> senderEventLog = senderClient.getEventLog(caseId, destinationId);
             final List<Event> senderEvents = senderEventLog.stream().map(EventLogEntry::getEvent).collect(Collectors.toList());
 
             // Then
@@ -167,6 +237,9 @@ public class EventLogIT extends IntegrationTestBase {
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
+        final SenderClient senderClient = ClientFactory.getSenderClient(config);
+        final SubscriberClient subscriberClient = ClientFactory.getSubscriberClient(config);
+
         final var submission = SendableSubmission.Builder()
                 .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                 .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
@@ -174,22 +247,23 @@ public class EventLogIT extends IntegrationTestBase {
                 .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                 .build();
 
-        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+        final var sentSubmission = senderClient.send(submission);
 
         Assertions.assertNotNull(sentSubmission);
 
-        final var receivedSubmission = ClientFactory.getSubscriberClient(config)
+        final var receivedSubmission = subscriberClient
                 .requestSubmission(sentSubmission.getSubmissionId());
 
         Assertions.assertNotNull(receivedSubmission);
 
+
         // When
         Awaitility.await().atMost(Duration.ofSeconds(30)).until(() ->
         {
             final UUID destinationId = receivedSubmission.getDestinationId();
             final UUID caseId = receivedSubmission.getCaseId();
 
-            final List<EventLogEntry> subscriberEventLog = ClientFactory.getSubscriberClient(config).getEventLog(caseId, destinationId);
+            final List<EventLogEntry> subscriberEventLog = subscriberClient.getEventLog(caseId, destinationId);
             final List<Event> subscriberEvents = subscriberEventLog.stream().map(EventLogEntry::getEvent).collect(Collectors.toList());
 
             // Then
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
index 0a2388502585d03a642ee8a38ba8a4c3a3a5f0d3..3e890f51f841946c7a79af91cd053041daa444bd 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
@@ -8,7 +8,6 @@ import dev.fitko.fitconnect.api.config.SubscriberConfig;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 import dev.fitko.fitconnect.client.SubscriberClient;
 import dev.fitko.fitconnect.client.factory.ClientFactory;
-import org.junit.jupiter.api.BeforeAll;
 
 import java.util.List;
 import java.util.Map;
@@ -23,8 +22,7 @@ public class IntegrationTestBase {
     private static final String selfServicePortalUrl = "https://portal.auth-testing.fit-connect.fitko.dev";
     private static final String submissionBaseUrl = "https://submission-api-testing.fit-connect.fitko.dev";
 
-    @BeforeAll
-    static void cleanupSubmissionByRejecting() {
+    public static void cleanupTestSubmissions() {
 
         final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));
         final Problem problem = new Problem(SCHEMA_URL + "technical-error", "cleanup", "submission-cleanup", "other");
@@ -32,7 +30,7 @@ public class IntegrationTestBase {
 
         subscriberClient.getAvailableSubmissionsForDestination(destinationId).forEach((s -> {
             try {
-                subscriberClient.requestSubmission(s.getSubmissionId()).rejectSubmission(List.of(problem));
+                subscriberClient.rejectSubmission(s, List.of(problem));
             } catch (final Exception e) {
                 //continue
             }
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
index 6d37a93e509824eef54547d6099aff91ca8b3543..0be4adb0af94dbd8949bee321fead0bc93014419 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
@@ -19,7 +19,6 @@ import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.factory.ClientFactory;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
-import dev.fitko.fitconnect.client.sender.model.AttachmentPayload;
 import dev.fitko.fitconnect.client.sender.model.EncryptedAttachment;
 import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
@@ -31,6 +30,7 @@ import org.apache.tika.mime.MimeTypes;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.io.File;
@@ -51,6 +51,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 @EnableIfEnvironmentVariablesAreSet
 public class SenderClientIT extends IntegrationTestBase {
 
+    @BeforeEach
+    public void cleanup(){
+        cleanupTestSubmissions();
+    }
+
     @Test
     void testSendAndConfirmCycle() {
 
@@ -64,7 +69,7 @@ public class SenderClientIT extends IntegrationTestBase {
                 .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "text/plain"))
                 .addAttachment(Attachment.fromByteArray("attachment data".getBytes(), "text/plain"))
                 .addAttachment(Attachment.fromString("attachment data", "text/plain"))
-                .setReplyChannel(ReplyChannel.fromEmail("test@mail.org"))
+                .setReplyChannel(ReplyChannel.fromDeMail("test@mail.org"))
                 .build();
 
         final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
@@ -82,7 +87,7 @@ public class SenderClientIT extends IntegrationTestBase {
         assertThat(receivedSubmission.getDataSchemaUri(), is(URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json")));
         assertThat(receivedSubmission.getDataMimeType(), is("application/json"));
         assertThat(receivedSubmission.getAttachments(), hasSize(3));
-        assertThat(receivedSubmission.getMetadata().getReplyChannel(), is(ReplyChannel.fromEmail("test@mail.org")));
+        assertThat(receivedSubmission.getMetadata().getReplyChannel(), is(ReplyChannel.fromDeMail("test@mail.org")));
         assertThat(new String(receivedSubmission.getAttachments().get(0).getDataAsBytes()), is("Test attachment"));
     }
 
@@ -120,25 +125,18 @@ public class SenderClientIT extends IntegrationTestBase {
         data.setHash(dataHash);
         data.setSubmissionSchema(submissionSchema);
 
-        final var attachmentPayload = AttachmentPayload.builder()
-                .encryptedData(encryptedAttachment)
-                .mimeType(MimeTypes.PLAIN_TEXT)
-                .attachmentId(UUID.randomUUID())
-                .hashedData(cryptoService.hashBytes(attachmentData.getBytes(StandardCharsets.UTF_8)))
-                .build();
-
         final var publicServiceType = new PublicServiceType();
         publicServiceType.setName("Test Service");
         publicServiceType.setIdentifier("urn:de:fim:leika:leistung:99400048079000");
 
         final var attachment = new ApiAttachment();
-        attachment.setAttachmentId(attachmentPayload.getAttachmentId());
+        attachment.setAttachmentId(UUID.randomUUID());
         attachment.setPurpose(Purpose.ATTACHMENT);
-        attachment.setFilename(attachmentPayload.getFileName());
-        attachment.setMimeType(attachmentPayload.getMimeType());
+        attachment.setFilename(attachmentFile.getName());
+        attachment.setMimeType(MimeTypes.PLAIN_TEXT);
 
         final var attachmentHash = new Hash();
-        attachmentHash.setContent(attachmentPayload.getHashedData());
+        attachmentHash.setContent(cryptoService.hashBytes(attachmentData.getBytes(StandardCharsets.UTF_8)));
         attachmentHash.setSignatureType(SignatureType.SHA_512);
         attachment.setHash(attachmentHash);
 
@@ -159,7 +157,7 @@ public class SenderClientIT extends IntegrationTestBase {
                 .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                 .setEncryptedMetadata(encryptedMetadata)
                 .setEncryptedData(encryptedData)
-                .addEncryptedAttachment(new EncryptedAttachment(attachmentPayload.getAttachmentId(), attachmentPayload.getEncryptedData()))
+                .addEncryptedAttachment(new EncryptedAttachment(attachment.getAttachmentId(), encryptedAttachment))
                 .build();
 
         final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
index b4eb69b484a93e2a9fb36855bef3c0e0060be87e..2c8dd6bda70482d4c11fb259538759da9b200387 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
@@ -3,21 +3,34 @@ package dev.fitko.fitconnect.integrationtests;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
+import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
 import dev.fitko.fitconnect.integrationtests.condition.EnableIfEnvironmentVariablesAreSet;
-import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 @EnableIfEnvironmentVariablesAreSet
 public class SubscriberClientIT extends IntegrationTestBase {
 
+    @BeforeEach
+    public void cleanup(){
+        cleanupTestSubmissions();
+    }
+
     @Test
     void testListSubmissionsForDestination() {
 
@@ -46,8 +59,8 @@ public class SubscriberClientIT extends IntegrationTestBase {
         final var sentSubmissionOne = senderClient.send(submissionOne);
         final var sentSubmissionTwo = senderClient.send(submissionTwo);
 
-        Assertions.assertNotNull(sentSubmissionOne);
-        Assertions.assertNotNull(sentSubmissionTwo);
+        assertNotNull(sentSubmissionOne);
+        assertNotNull(sentSubmissionTwo);
 
         // When
         final Set<SubmissionForPickup> submissions = subscriberClient.getAvailableSubmissionsForDestination(destinationId);
@@ -57,10 +70,44 @@ public class SubscriberClientIT extends IntegrationTestBase {
 
         final List<UUID> submissionIds = submissions.stream().map(SubmissionForPickup::getSubmissionId).collect(Collectors.toList());
 
-        MatcherAssert.assertThat(submissionIds, Matchers.hasItems(sentSubmissionOne.getSubmissionId(), sentSubmissionTwo.getSubmissionId()));
+        assertThat(submissionIds, Matchers.hasItems(sentSubmissionOne.getSubmissionId(), sentSubmissionTwo.getSubmissionId()));
 
         // remove by confirming
         subscriberClient.requestSubmission(sentSubmissionOne.getSubmissionId()).acceptSubmission();
         subscriberClient.requestSubmission(sentSubmissionTwo.getSubmissionId()).acceptSubmission();
     }
+
+    @Test
+    void testReceiveSingleSubmission() {
+
+        // Given
+        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
+
+        final var senderClient = ClientFactory.getSenderClient(config);
+        final var subscriberClient = ClientFactory.getSubscriberClient(config);
+
+        final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));
+        final String leikaKey = "urn:de:fim:leika:leistung:99400048079000";
+        final String serviceName = "Test Service";
+
+        final var submission = SendableSubmission.Builder()
+                .setDestination(destinationId)
+                .setServiceType(leikaKey, serviceName)
+                .setJsonData("{ \"data\": \"Beispiel Fachdaten 1\" }")
+                .addAttachment(Attachment.fromString("foo", "plain/text"))
+                .build();
+
+        final var sentSubmission = senderClient.send(submission);
+
+        assertNotNull(sentSubmission);
+
+        // When
+        final ReceivedSubmission receivedSubmission = subscriberClient.requestSubmission(sentSubmission.getSubmissionId());
+
+        // Then
+        assertNotNull(receivedSubmission);
+        assertThat(receivedSubmission.getAttachments(), hasSize(1));
+        assertThat(receivedSubmission.getAttachments().get(0).getDataAString(StandardCharsets.UTF_8), is("foo"));
+        assertThat(receivedSubmission.getDataAsString(), is("{ \"data\": \"Beispiel Fachdaten 1\" }"));
+    }
 }
diff --git a/pom.xml b/pom.xml
index 639b1f1aef0bec26d459bd7b16fb39d7d0366e4e..d32b872daaa04a464d10d5d17de1404e29b99ce0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,10 +66,10 @@
         <jackson-annotations.version>2.14.2</jackson-annotations.version>
         <lombock.version>1.18.26</lombock.version>
         <logback.version>1.4.6</logback.version>
-        <slf4j.version>2.0.6</slf4j.version>
+        <slf4j.version>2.0.7</slf4j.version>
         <jcommander.version>1.82</jcommander.version>
         <apache-tika.version>2.7.0</apache-tika.version>
-        <spring-web.version>5.3.25</spring-web.version>
+        <spring-web.version>5.3.26</spring-web.version>
         <snakeyaml.version>2.0</snakeyaml.version>
         <open-csv.version>5.7.1</open-csv.version>
         <json-schema-validator.version>1.0.78</json-schema-validator.version>