diff --git a/README.md b/README.md
index 9bdb7e5330add876fd370eb0940a444e8fbebaa7..1ccc415d325acce2628424d5b6aec1c940826678 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,34 @@
-## FitCo Java SDK
\ No newline at end of file
+## FIT-Connect Java SDK
+
+### Project Setup Info
+
+- Java 11 (LTS)
+- Maven
+- Junit 5
+- (Guice DI)
+
+### Build
+
+To build the maven artifact, run: `mvn clean package`
+
+### Tests
+
+To run all tests, run: `mvn test`
+
+### External Configuration
+
+Configuration properties e.g. for REST-API urls and proxy settings can be found in
+the [HOCON](https://github.com/lightbend/config/blob/main/HOCON.md)
+config ``sdk.conf`` within the root folder of the project.
+
+### Documentation
+
+See the projects module documentation for more specific information:
+
+* [API Module](api/README.md)
+* [Core Module](core/README.md)
+* [Client Module](client/README.md)
+* [Dependency Module](dependency/README.md)
+* [Open-API Module](open-api/README.md)
+
+As well as the official API docs: [FIT-Connect Documentation](https://docs.fitko.de/fit-connect/docs/)
diff --git a/api/README.md b/api/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e5ba47e0ed4de6004500bcce4d0ba11d82431eca
--- /dev/null
+++ b/api/README.md
@@ -0,0 +1,104 @@
+## API Module
+
+The API-module contains interfaces and domain model value classes that provide the basic functionality to build an
+SDK client.
+
+### Structure
+
+- **api.domain** - contains all model classes and domain related POJOs
+- **api.exceptions** - all use case specific exceptions thrown by services
+- **api.services** - all services to authenticate, encrypt, validate and perform REST-requests
+
+There are two service facade interfaces that provide a client centric wrapper around the underlying services,
+
+- **Sender** - create a submission, announce attachments, encrypt and send the submission including metadata
+- **Subscriber** - poll, receive and decrypt submissions and confirm their valid transmission
+
+### Service Architecture
+
+Apart from the ClientFactory the overall service architecture focuses on composition and has no inherited dependencies.
+
+```mermaid
+classDiagram
+
+  class CryptoService{
+    + decryptString
+    + decryptBytes 
+    + encryptString
+    + encryptBytes 
+   }
+
+
+   class CertificateValidator{
+        + validatePublicKey
+  }
+
+  class MetadataValidator{
+    + validateMetadataSchema
+    + validateMetadataHashValues
+  }
+
+  class MetadataService{
+    + createMetadata
+  }
+
+  class OAuthService{
+   + authenticate
+  }
+
+  class SenderFacade {
+    + retrieveOAuthToken
+    + validatePublicKey
+    + encryptSubmissionData
+    + encryptAttachment
+    + createMetadata
+    + createSubmission
+    + sendSubmission
+    + uploadAttachments
+    + createAttachment
+    + getEncryptionKeyForDestination
+  }
+
+   class SubscriberFacade {
+    + retrieveOAuthToken
+    + decryptStringContent
+    + pollAvailableSubmissions
+    + getSubmission
+    + fetchAttachments
+    + validateEventLog
+    + validateMetadata
+    + validateData
+    + validateAttachments
+    + confirmValidSubmission
+  }
+  
+  class SubscriberClient{
+       SubscriberFacade
+  }
+  
+  class SenderClient{
+        SenderFacade
+  }
+ 
+    class ClientFactory {
+        AppplicationConfig
+  }
+  
+ 
+ClientFactory  ..> SenderClient : Constructs
+ClientFactory  ..> SubscriberClient : Constructs
+
+SenderClient ..> SenderFacade : Uses
+SubscriberClient ..> SubscriberFacade : Uses
+
+SenderFacade ..> CertificateValidator : Uses
+
+SenderFacade ..> MetadataService : Uses
+SenderFacade ..> OAuthService : Uses
+SenderFacade ..> CryptoService : Uses
+
+SubscriberFacade ..> OAuthService : Uses
+SubscriberFacade ..> CryptoService : Uses
+SubscriberFacade ..> MetadataValidator : Uses
+
+```
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..543e9c31bd3375e084aa6183e3ef68af47613aba
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>de.fitko.fitconnect.sdk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/auth/OAuthToken.java b/api/src/main/java/de/fitko/fitconnect/api/domain/auth/OAuthToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a53a6fe93a4c944b273562aa6d4c4eecd68f367
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/auth/OAuthToken.java
@@ -0,0 +1,22 @@
+package de.fitko.fitconnect.api.domain.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class OAuthToken {
+    @JsonProperty("access_token")
+    String accessToken;
+    @JsonProperty("scope")
+    String scope;
+    @JsonProperty("token_type")
+    String tokenType;
+    @JsonProperty("error")
+    String error;
+    @JsonProperty("error_description")
+    String errorDescription;
+    @JsonProperty("expires_in")
+    Integer expiresIn;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java
new file mode 100644
index 0000000000000000000000000000000000000000..21f58696efd1b89283e25d232d8136374df4cb8e
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/destination/Destination.java
@@ -0,0 +1,11 @@
+package de.fitko.fitconnect.api.domain.model.destination;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.UUID;
+
+public class Destination {
+
+    @JsonProperty("encryptionKid")
+    UUID encryptionKid;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AdditionalReferenceInfo.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AdditionalReferenceInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..07e92aadc53f3af2c8d244ce1cbb9b7cde76bf0c
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AdditionalReferenceInfo.java
@@ -0,0 +1,30 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "senderReference",
+        "applicationDate"
+})
+@Getter
+@Setter
+public class AdditionalReferenceInfo {
+
+    @JsonProperty("senderReference")
+    String senderReference;
+
+    @JsonProperty("applicationDate")
+    String applicationDate;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AuthenticationInformation.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AuthenticationInformation.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb48cfe5c989c97469cbdd8e0fcea1e4d4a7eaaf
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/AuthenticationInformation.java
@@ -0,0 +1,9 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class AuthenticationInformation {
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fbebed0cd06063788cd3eb553ccbc4d6854e17d
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ContentStructure.java
@@ -0,0 +1,34 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "data",
+        "attachments"
+})
+@Getter
+@Setter
+@Builder
+public class ContentStructure {
+
+    @JsonProperty("data")
+    Data data;
+
+    @JsonProperty("attachments")
+    List<Attachment> attachments;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/Metadata.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/Metadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1df6c5f3ee24f36eaa59d7d15fd8011e2cf9703
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/Metadata.java
@@ -0,0 +1,48 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "contentStructure",
+        "publicServiceType",
+        "authenticationInformation",
+        "paymentInformation",
+        "replyChannel",
+        "additionalReferenceInfo"
+})
+@Getter
+@Setter
+@Builder
+public class Metadata {
+    @JsonProperty("contentStructure")
+    ContentStructure contentStructure;
+
+    @JsonProperty("publicServiceType")
+    PublicServiceType publicServiceType;
+
+    @JsonProperty("authenticationInformation")
+    List<AuthenticationInformation> authenticationInformation;
+
+    @JsonProperty("paymentInformation")
+    PaymentInformation paymentInformation;
+
+    @JsonProperty("replyChannel")
+    ReplyChannel replyChannel;
+
+    @JsonProperty("additionalReferenceInfo")
+    AdditionalReferenceInfo additionalReferenceInfo;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PaymentInformation.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PaymentInformation.java
new file mode 100644
index 0000000000000000000000000000000000000000..27b7c019e6f4f22b757bb7cde61f3bed4b11a49d
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PaymentInformation.java
@@ -0,0 +1,9 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class PaymentInformation {
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PublicServiceType.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PublicServiceType.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb29a6d1c50fe39fc0b8beb399d46b736a2ff7ea
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/PublicServiceType.java
@@ -0,0 +1,34 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "name",
+        "description",
+        "identifier"
+})
+@Getter
+@Setter
+public class PublicServiceType {
+
+    @JsonProperty("name")
+    String name;
+
+    @JsonProperty("description")
+    String description;
+
+    @JsonProperty("identifier")
+    String identifier;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ReplyChannel.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ReplyChannel.java
new file mode 100644
index 0000000000000000000000000000000000000000..233c7e6ba17ff6098dbf332eb033f1c70196e07b
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/ReplyChannel.java
@@ -0,0 +1,9 @@
+package de.fitko.fitconnect.api.domain.model.metadata;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ReplyChannel {
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java
new file mode 100644
index 0000000000000000000000000000000000000000..c88f7b1ac64774e86aaedee57f2767889b0b3abb
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Attachment.java
@@ -0,0 +1,57 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Hash__1;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Signature__1;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.Value;
+
+import java.util.Map;
+import java.util.UUID;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "hash",
+        "signature",
+        "purpose",
+        "filename",
+        "description",
+        "mimeType",
+        "attachmentId"
+})
+@Getter
+@Setter
+@Builder
+public class Attachment {
+
+    @JsonProperty("hash")
+    Hash__1 hash;
+
+    @JsonProperty("signature")
+    Signature__1 signature;
+
+    @JsonProperty("purpose")
+    Purpose purpose;
+
+    @JsonProperty("filename")
+    String filename;
+
+    @JsonProperty("description")
+    String description;
+
+    @JsonProperty("mimeType")
+    String mimeType;
+
+    @JsonProperty("attachmentId")
+    UUID attachmentId;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Purpose.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Purpose.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5726ea3875d4c751fbd77058fb39489350ffcb1
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/Purpose.java
@@ -0,0 +1,46 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum Purpose {
+
+    FORM("form"),
+    ATTACHMENT("attachment"),
+    REPORT("report");
+    private final String value;
+    private final static Map<String, Purpose> CONSTANTS = new HashMap<>();
+
+    static {
+        for (Purpose c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    Purpose(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static Purpose fromValue(String value) {
+        Purpose constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/EidasAdesProfile.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/EidasAdesProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..21199aef3400f361a95797a6c6b1ffe13307e63e
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/EidasAdesProfile.java
@@ -0,0 +1,48 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment.signature;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum EidasAdesProfile {
+
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_B("http://uri.etsi.org/ades/191x2/level/baseline/B-B#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_T("http://uri.etsi.org/ades/191x2/level/baseline/B-T#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LT("http://uri.etsi.org/ades/191x2/level/baseline/B-LT#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LTA("http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#");
+    private final String value;
+    private final static Map<String, EidasAdesProfile> CONSTANTS = new HashMap<>();
+
+    static {
+        for (EidasAdesProfile c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    EidasAdesProfile(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static EidasAdesProfile fromValue(String value) {
+        EidasAdesProfile constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Hash__1.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Hash__1.java
new file mode 100644
index 0000000000000000000000000000000000000000..3522e8d5dfacbf4535829abe9df90b9932a87539
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Hash__1.java
@@ -0,0 +1,30 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment.signature;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "type",
+        "content"
+})
+@Getter
+@Setter
+public class Hash__1 {
+
+    @JsonProperty("type")
+    Type type;
+
+    @JsonProperty("content")
+    String content;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/SignatureFormat.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/SignatureFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9c613efe91ad281f4ead385a3652a66d0287846
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/SignatureFormat.java
@@ -0,0 +1,49 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment.signature;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum SignatureFormat {
+
+    CMS("cms"),
+    XML("xml"),
+    PDF("pdf"),
+    ASIC("asic"),
+    JSON("json");
+    private final String value;
+    private final static Map<String, SignatureFormat> CONSTANTS = new HashMap<>();
+
+    static {
+        for (SignatureFormat c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    SignatureFormat(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static SignatureFormat fromValue(String value) {
+        SignatureFormat constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Signature__1.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Signature__1.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2288869acc3be85a2ec6d2b4bb5bdd2d4ad211a
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Signature__1.java
@@ -0,0 +1,39 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment.signature;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "signatureFormat",
+        "eidasAdesProfile",
+        "detachedSignature",
+        "content"
+})
+@Getter
+@Setter
+public class Signature__1 {
+
+    @JsonProperty("signatureFormat")
+    SignatureFormat signatureFormat;
+
+    @JsonProperty("eidasAdesProfile")
+    EidasAdesProfile eidasAdesProfile;
+
+    @JsonProperty("detachedSignature")
+    Boolean detachedSignature;
+
+    @JsonProperty("content")
+    String content;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Type.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Type.java
new file mode 100644
index 0000000000000000000000000000000000000000..17b4660ee90a83fe8f0773d353b75b597f85c6fa
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/attachment/signature/Type.java
@@ -0,0 +1,45 @@
+package de.fitko.fitconnect.api.domain.model.metadata.attachment.signature;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum Type {
+
+    SHA_512("sha512");
+    private final String value;
+    private final static Map<String, Type> CONSTANTS = new HashMap<>();
+
+    static {
+        for (Type c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    Type(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static Type fromValue(String value) {
+        Type constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Data.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Data.java
new file mode 100644
index 0000000000000000000000000000000000000000..58e9dc5d86a6be92f44055ce3e1394759694b80e
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Data.java
@@ -0,0 +1,36 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "signature",
+        "hash",
+        "submissionSchema"
+})
+@Getter
+@Setter
+@Builder
+public class Data {
+
+    @JsonProperty("signature")
+    Signature signature;
+
+    @JsonProperty("hash")
+    Hash hash;
+
+    @JsonProperty("submissionSchema")
+    SubmissionSchema submissionSchema;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/EidasAdesProfile.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/EidasAdesProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc8843b28eb31a9b6e4ac09052f3ce3e12d35dfb
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/EidasAdesProfile.java
@@ -0,0 +1,50 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum EidasAdesProfile {
+
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_B("http://uri.etsi.org/ades/191x2/level/baseline/B-B#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_T("http://uri.etsi.org/ades/191x2/level/baseline/B-T#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LT("http://uri.etsi.org/ades/191x2/level/baseline/B-LT#"),
+    HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LTA("http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#");
+
+    private final String value;
+    private final static Map<String, EidasAdesProfile> CONSTANTS = new HashMap<>();
+
+    static {
+        for (EidasAdesProfile c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    EidasAdesProfile(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static EidasAdesProfile fromValue(String value) {
+        EidasAdesProfile constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Hash.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Hash.java
new file mode 100644
index 0000000000000000000000000000000000000000..94475c30808ea346a22b1fa2dec4c7e0b6df61bf
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Hash.java
@@ -0,0 +1,29 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "type",
+        "content"
+})
+@Getter
+@Setter
+public class Hash {
+
+    @JsonProperty("type")
+    Type type;
+
+    @JsonProperty("content")
+    String content;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/MimeType.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/MimeType.java
new file mode 100644
index 0000000000000000000000000000000000000000..cf210d4837c0b4114e00695a3f493123a15b1fa0
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/MimeType.java
@@ -0,0 +1,46 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum MimeType {
+
+    APPLICATION_JSON("application/json"),
+    APPLICATION_XML("application/xml");
+    private final String value;
+    private final static Map<String, MimeType> CONSTANTS = new HashMap<>();
+
+    static {
+        for (MimeType c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    MimeType(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static MimeType fromValue(String value) {
+        MimeType constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Signature.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Signature.java
new file mode 100644
index 0000000000000000000000000000000000000000..dba35defd74ff1ec7dfe8c1719fa8c87c01dce00
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Signature.java
@@ -0,0 +1,38 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "signatureFormat",
+        "eidasAdesProfile",
+        "detachedSignature",
+        "content"
+})
+@Getter
+@Setter
+public class Signature {
+
+    @JsonProperty("signatureFormat")
+    SignatureFormat signatureFormat;
+
+    @JsonProperty("eidasAdesProfile")
+    EidasAdesProfile eidasAdesProfile;
+
+    @JsonProperty("detachedSignature")
+    Boolean detachedSignature;
+
+    @JsonProperty("content")
+    String content;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SignatureFormat.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SignatureFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1676eb7bb87c606403680c7637f60d7d95b06fe
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SignatureFormat.java
@@ -0,0 +1,49 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum SignatureFormat {
+
+    CMS("cms"),
+    XML("xml"),
+    PDF("pdf"),
+    ASIC("asic"),
+    JSON("json");
+    private final String value;
+    private final static Map<String, SignatureFormat> CONSTANTS = new HashMap<>();
+
+    static {
+        for (SignatureFormat c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    SignatureFormat(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static SignatureFormat fromValue(String value) {
+        SignatureFormat constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SubmissionSchema.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SubmissionSchema.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d3b62bcdb7676e41bf967987fa80abefaab49ba
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/SubmissionSchema.java
@@ -0,0 +1,30 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.net.URI;
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({
+        "schemaUri",
+        "mimeType"
+})
+@Getter
+@Setter
+public class SubmissionSchema {
+
+    @JsonProperty("schemaUri")
+    URI schemaUri;
+
+    @JsonProperty("mimeType")
+    MimeType mimeType;
+
+    @JsonIgnore
+    Map<String, Object> additionalProperties;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Type.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Type.java
new file mode 100644
index 0000000000000000000000000000000000000000..060804d68bd84425a334e5d890f1aa5afa497dda
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/metadata/data/Type.java
@@ -0,0 +1,45 @@
+package de.fitko.fitconnect.api.domain.model.metadata.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum Type {
+
+    SHA_512("sha512");
+    private final String value;
+    private final static Map<String, Type> CONSTANTS = new HashMap<>();
+
+    static {
+        for (Type c : values()) {
+            CONSTANTS.put(c.value, c);
+        }
+    }
+
+    Type(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return this.value;
+    }
+
+    @JsonValue
+    public String value() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static Type fromValue(String value) {
+        Type constant = CONSTANTS.get(value);
+        if (constant == null) {
+            throw new IllegalArgumentException(value);
+        } else {
+            return constant;
+        }
+    }
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Callback.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Callback.java
new file mode 100644
index 0000000000000000000000000000000000000000..168430b42d770315cd25bd7d7926787729d26f78
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Callback.java
@@ -0,0 +1,20 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.Value;
+
+import java.net.URI;
+
+@Getter
+@Setter
+@Value
+public class Callback {
+
+    @JsonProperty("uri")
+    URI uri;
+
+    @JsonProperty("secret")
+    String secret;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/ServiceType.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/ServiceType.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdd7cd2deec1ba260629284650ba20d1270bcac4
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/ServiceType.java
@@ -0,0 +1,21 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.Value;
+
+@Getter
+@Setter
+@Value
+public class ServiceType {
+
+    @JsonProperty("name")
+    String name;
+
+    @JsonProperty("description")
+    String description;
+
+    @JsonProperty("identifier")
+    String identifier;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java
new file mode 100644
index 0000000000000000000000000000000000000000..1af6a01ac1e771fb19fd4b1c43e665f479bf794b
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/Submission.java
@@ -0,0 +1,39 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Value
+@Builder
+public class Submission {
+
+    @JsonProperty("destinationId")
+    private UUID destinationId;
+
+    @JsonProperty("submissionId")
+    private UUID submissionId;
+
+    @JsonProperty("caseId")
+    private UUID caseId;
+
+    @JsonProperty("attachments")
+    private List<UUID> attachments = new ArrayList<>();
+
+    @JsonProperty("serviceType")
+    private ServiceType serviceType;
+
+    @JsonProperty("callback")
+    private Callback callback;
+
+    @JsonProperty("encryptedMetadata")
+    private String encryptedMetadata;
+
+    @JsonProperty("encryptedData")
+    private String encryptedData;
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionForPickup.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionForPickup.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c179568b54a015dda2d6828f2e752b2825f4848
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionForPickup.java
@@ -0,0 +1,19 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Value;
+
+import java.util.UUID;
+
+@Value
+public class SubmissionForPickup {
+
+    @JsonProperty("destinationId")
+    private final UUID destinationId;
+
+    @JsonProperty("submissionId")
+    private final UUID submissionId;
+
+    @JsonProperty("caseId")
+    private final UUID caseId;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionSubmit.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionSubmit.java
new file mode 100644
index 0000000000000000000000000000000000000000..a90e0398aaf566908a6e9605f6a313245e36ea17
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionSubmit.java
@@ -0,0 +1,34 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.Value;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Getter
+@Setter
+@Value
+@Builder
+public class SubmissionSubmit {
+
+    @JsonProperty("destinationId")
+    UUID destinationId;
+
+    @JsonProperty("destinationId")
+    Optional<UUID> caseId;
+
+    @JsonProperty("announcedAttachments")
+    List<UUID> announcedAttachments;
+
+    @JsonProperty("serviceType")
+    ServiceType serviceType;
+
+    @JsonProperty("callback")
+    Callback callback;
+
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionsForPickup.java b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionsForPickup.java
new file mode 100644
index 0000000000000000000000000000000000000000..54f0f14a0ebd3e2d4295e9d958401ccfcd8d16dd
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/model/submission/SubmissionsForPickup.java
@@ -0,0 +1,25 @@
+package de.fitko.fitconnect.api.domain.model.submission;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Value;
+
+import java.util.Collections;
+import java.util.List;
+
+@Value
+@Builder
+public class SubmissionsForPickup{
+    @JsonProperty("count")
+    int count;
+
+    @JsonProperty("offset")
+    int offset;
+
+    @JsonProperty("totalCount")
+    int totalCount;
+
+    @JsonProperty("submissions")
+    List<SubmissionForPickup> submissions = Collections.emptyList();
+}
\ No newline at end of file
diff --git a/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java b/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c52d861485b04bf31e73c9ffb9fca61f4455b7d
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/domain/validation/ValidationResult.java
@@ -0,0 +1,39 @@
+package de.fitko.fitconnect.api.domain.validation;
+
+/**
+ * Wrapper for validations including an exception
+ */
+public class ValidationResult {
+
+    private final boolean isValid;
+    private Exception error;
+
+    private ValidationResult(final boolean isValid) {
+        this.isValid = isValid;
+    }
+
+    private ValidationResult(final boolean isValid, final Exception exception) {
+        this.isValid = isValid;
+        this.error = exception;
+    }
+
+    public static ValidationResult ok() {
+        return new ValidationResult(true);
+    }
+
+    public static ValidationResult error(final Exception exception) {
+        return new ValidationResult(false, exception);
+    }
+
+    public boolean isValid() {
+        return this.isValid;
+    }
+
+    public boolean hasError() {
+        return !isValid || error != null;
+    }
+
+    public Exception getError() {
+        return this.error;
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentCreationException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentCreationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..64bb8ac02dac6afb5ea278e3e4d1e48cb54cb720
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentCreationException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class AttachmentCreationException extends RuntimeException {
+
+    public AttachmentCreationException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentUploadException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentUploadException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f83dee7797b52217b6cea053de53d761e85fffd9
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AttachmentUploadException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class AttachmentUploadException extends RuntimeException {
+
+    public AttachmentUploadException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/AuthenticationException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AuthenticationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..560737265afc09302ab155f1746544604ca45ecc
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/AuthenticationException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class AuthenticationException extends RuntimeException {
+
+    public AuthenticationException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/ClientNotAuthenticatedException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/ClientNotAuthenticatedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..70ed2b4fb8731faf0c0a609db32f3f2da2b37771
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/ClientNotAuthenticatedException.java
@@ -0,0 +1,13 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class ClientNotAuthenticatedException extends RuntimeException {
+
+
+    public ClientNotAuthenticatedException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public ClientNotAuthenticatedException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/DecryptionException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/DecryptionException.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcaadebe61442569ecec9d827b6c71b1611e7a28
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/DecryptionException.java
@@ -0,0 +1,12 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class DecryptionException extends RuntimeException {
+
+    public DecryptionException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public DecryptionException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/EncryptionException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/EncryptionException.java
new file mode 100644
index 0000000000000000000000000000000000000000..664a3ba0a0b95cb90b53d5a60ea1ba50b00be512
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/EncryptionException.java
@@ -0,0 +1,13 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class EncryptionException extends RuntimeException {
+
+    public EncryptionException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public EncryptionException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
+
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/InitializationException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/InitializationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdb9be9134d90049d488b0bb4334c71d16f09699
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/InitializationException.java
@@ -0,0 +1,12 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class InitializationException extends RuntimeException {
+
+    public InitializationException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public InitializationException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/InvalidPublicKeyException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/InvalidPublicKeyException.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ab43a95394d59aa878c73dce249d5141c7f50bd
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/InvalidPublicKeyException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class InvalidPublicKeyException extends RuntimeException {
+
+    public InvalidPublicKeyException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..15499532d2f081ae067a5b68c01ea8ada87a5ffe
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class KeyNotRetrievedException extends RuntimeException {
+
+    public KeyNotRetrievedException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a045c1dac26dce205414d683ac886f9da8da68b4
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class MetadataNotCreatedException extends RuntimeException {
+
+    public MetadataNotCreatedException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9487409e6b750a4ab5e8975d5f42d20b5bf667df
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class SubmissionNotCreatedException extends RuntimeException {
+
+    public SubmissionNotCreatedException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotSentException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotSentException.java
new file mode 100644
index 0000000000000000000000000000000000000000..63dfd3aac77564fe8fe8f06c3183aefd9d8ee840
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/SubmissionNotSentException.java
@@ -0,0 +1,8 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class SubmissionNotSentException extends RuntimeException {
+
+    public SubmissionNotSentException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/exceptions/ValidationException.java b/api/src/main/java/de/fitko/fitconnect/api/exceptions/ValidationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..76c364c3b38e29ba5292eabb5b29002f98d2c787
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/exceptions/ValidationException.java
@@ -0,0 +1,13 @@
+package de.fitko.fitconnect.api.exceptions;
+
+public class ValidationException extends RuntimeException {
+
+
+    public ValidationException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public ValidationException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/package-info.java b/api/src/main/java/de/fitko/fitconnect/api/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..53cbfecdec53d0b908f555af59e30104254cd417
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * This module is providing the internal sdk api
+ * <p>
+ *
+ * </p>
+ *
+ * @since 1.0
+ * @author FitConnect
+ * @version 1.0
+ */
+package de.fitko.fitconnect.api;
\ No newline at end of file
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java b/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java
new file mode 100644
index 0000000000000000000000000000000000000000..52cedd8f6497005e2a14ded257f8b496d007ceb5
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/Sender.java
@@ -0,0 +1,148 @@
+package de.fitko.fitconnect.api.services;
+
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.*;
+
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A technical system that creates a submission via the FIT-Connect Submission API.
+ * <p>
+ * The Sender acts as a common interface wrapping all client functionality for authenticating <p>
+ * and creating a valid {@link SubmissionSubmit} including encrypted {@link Data} and {@link Attachment}s
+ *
+ * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/overview">Sending Submissions</a>
+ */
+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(final JWK publicKey);
+
+    /**
+     * Encrypts the submission data payload (json or xml) with JWE (JSON-Web-Encryption).
+     *
+     * @param publicKey the public encryption key for encryption of {@link Data}
+     * @param data      the {@link Data} payload that should be encrypted
+     * @return the JWE-encrypted {@link Data}
+     *
+     * @throws EncryptionException if the encryption fails due to an invalid key or corrupt data
+     */
+    Data encryptSubmissionData(final RSAKey publicKey, final Data data) throws EncryptionException;
+
+    /**
+     * Encrypts the submission attachment data with JWE (JSON-Web-Encryption).
+     *
+     * @param publicKey  the public encryption key for encryption of {@link Attachment}
+     * @param attachment the {@link Attachment} that should be encrypted
+     * @return the JWE-encrypted {@link Attachment}
+     *
+     * @throws EncryptionException
+     */
+    Attachment encryptAttachment(final RSAKey publicKey, final Attachment attachment) throws EncryptionException;
+
+    /**
+     * Authenticates the sender against the FIT-Connect API and retrieves an authentication token
+     *
+     * @param clientId     a unique identifier within the FIT-Connect platform environment
+     * @param clientSecret a secret that is only known by the application and the OAuth server
+     * @param scope        OAuth scope(s) that determine if a submission is accepted by the client
+     * @return {@link OAuthToken} that holds the access-token
+     *
+     * @throws {@link AuthenticationException} if an error occurred, including the original cause
+     */
+    OAuthToken retrieveOAuthToken(final String clientId, final String clientSecret, final String... scope) throws AuthenticationException;
+
+    /**
+     * Creates and uploads a {@link Metadata} that contains {@link Data}, {@link Attachment} and their hashes so a subscriber
+     * can verify the integrity.
+     *
+     * @param data        {@link Data} object of the json or xml payload
+     * @param attachments a list of {@link Attachment}s
+     * @return a valid {@link Metadata} object with hashed {@link Data} and {@link Attachment}s
+     */
+    Metadata createMetadata(final Data data, final List<Attachment> attachments) throws MetadataNotCreatedException;
+
+    /**
+     * Creates and uploads a {@link Metadata} that contains {@link Data}, {@link Attachment} and their hashes so a subscriber
+     * can verify the integrity.
+     *
+     * @param attachments a list of {@link Attachment}s
+     * @return a valid {@link Metadata} object with hashed {@link Data} and {@link Attachment}s
+     *
+     * @throws MetadataNotCreatedException if there was an error during the upload or on creation of the metadata object
+     */
+    Metadata createMetadata(final List<Attachment> attachments) throws MetadataNotCreatedException;
+
+    /**
+     * Creates and announces a new {@link SubmissionSubmit}.
+     *
+     * @param submission with a destinationId, a list of attachmentIds and a serviceType
+     * @return the announced submission
+     *
+     * @throws SubmissionNotCreatedException if the announcement of the submission failed, e.g. due to a technical problem
+     */
+    SubmissionForPickup createSubmission(SubmissionSubmit submission) throws SubmissionNotCreatedException;
+
+    /**
+     * Posts the announced {@link SubmissionSubmit}
+     *
+     * @param submissionId      unique identifier of the announced submission
+     * @param encryptedMetadata the encrypted {@link Metadata}
+     * @return the submission that has been submitted
+     *
+     * @throws SubmissionNotSentException if the sending process failed
+     */
+    SubmissionForPickup sendSubmission(UUID submissionId, Metadata encryptedMetadata) throws SubmissionNotSentException;
+
+    /**
+     * Uploads encrypted {@link Attachment}s to for given submission id.
+     *
+     * @param submissionId unique identifier of the  submission
+     * @param encryptedAttachments list of {@link Attachment}s
+     *
+     * @throws AttachmentUploadException
+     */
+    void uploadAttachments(UUID submissionId, List<Attachment> encryptedAttachments) throws AttachmentUploadException;
+
+    /**
+     * Creates a new {@link Attachment} from a given file.
+     *
+     * @param file file containing the {@link Attachment} payload.
+     * @return an {@link Attachment} that contains the hashed file content as string
+     *
+     * @throws AttachmentCreationException if the file could not be loaded
+     */
+    Attachment createAttachment(File file) throws AttachmentCreationException;
+
+    /**
+     * Retrieves the public encryption key for a destination.
+     *
+     * @param destinationId unique identifier of the destination
+     * @return the public key for encrypting {@link Metadata}, {@link Data} and {@link Attachment}s
+     *
+     * @throws KeyNotRetrievedException if a technical problem occurred or the destination id is not valid
+     */
+    RSAKey getEncryptionKeyForDestination(UUID destinationId) throws KeyNotRetrievedException;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java b/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java
new file mode 100644
index 0000000000000000000000000000000000000000..e42e89c3b1ece3d162b2f47f0e6891be6687aed0
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/Subscriber.java
@@ -0,0 +1,110 @@
+package de.fitko.fitconnect.api.services;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.Submission;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.AuthenticationException;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A technical system that accepts submissions on the administration side.
+ *
+ * @see <a href="https://docs.fitko.de/fit-connect/docs/receiving/overview">Receiving Submissions</a>
+ */
+public interface Subscriber {
+
+    /**
+     * Authenticates the subscriber against the FIT-Connect API and retrieves an authentication token
+     *
+     * @param clientId     a unique identifier within the FIT-Connect platform environment
+     * @param clientSecret a secret that is only known by the application and the OAuth server
+     * @param scope        OAuth scope(s) that determine if a submission is accepted by the client
+     * @return {@link OAuthToken} that holds the access-token
+     *
+     * @throws {@link AuthenticationException} if an error occurred, including the original cause
+     */
+    OAuthToken retrieveOAuthToken(final String clientId, final String clientSecret, final String... scope);
+
+    /**
+     * Decrypts JWE-encrypted string data.
+     *
+     * @param privateKey       the private key to decrypt the JWE-encrypted string
+     * @param encryptedContent the content that should be decrypted
+     * @return the decrypted content as byte[]
+     */
+    byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent);
+
+    /**
+     * Polls available {@link SubmissionSubmit}s for a given destinationId.
+     *
+     * @param destinationId restricts the query to a specific destination
+     * @param limit         number of submissions in result (max. is 500)
+     * @param offset        position in the dataset
+     * @return list of found {@link SubmissionSubmit}s
+     */
+    List<SubmissionForPickup> pollAvailableSubmissions(UUID destinationId, int limit, int offset);
+
+    /**
+     * Gets a {@link SubmissionSubmit}.
+     *
+     * @param submissionId the unique identifier of a {@link SubmissionSubmit}
+     * @return the requested {@link Submission}
+     */
+    Submission getSubmission(UUID submissionId);
+
+    /**
+     * Loads a list of {@link Attachment}s for a {@link Submission}.
+     *
+     * @param submissionId the unique identifier of a {@link Submission}
+     * @param attachmentIds a list of {@link Attachment} UUIDs
+     * @return list of {@link Attachment} objects
+     */
+    List<Attachment> fetchAttachments(UUID submissionId, List<UUID> attachmentIds);
+
+    /**
+     * Validates the structure and signature of a Security-Event-Token (SET).
+     *
+     * @param caseId unique identifier of a {@link Submission}s case
+     * @returna {@link ValidationResult}, contains an error if the SET is invalid
+     */
+    ValidationResult validateEventLog(UUID caseId);
+
+    /**
+     * Validates the {@link Metadata} structure against a given JSON-schema 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
+     */
+    ValidationResult validateMetadata(Metadata metadata, String schema);
+
+    /**
+     * Validates the {@link Data}.
+     *
+     * @param data the received data
+     * @return a {@link ValidationResult}, contains an error if the {@link Data} is invalid
+     */
+    ValidationResult validateData(Data data);
+
+    /**
+     * Validates the {@link Attachment}s.
+     *
+     * @param attachments the received list of attachments
+     * @return a {@link ValidationResult}, contains an error if the {@link Attachment}s are invalid
+     */
+    ValidationResult validateAttachments(List<Attachment> attachments);
+
+    /**
+     * Sends a confirmation event if the received submission matches all validations.
+     *
+     * @see <a href="https://docs.fitko.de/fit-connect/docs/receiving/process-and-acknowledge">Process And Acknowledge</a>
+     */
+    void confirmValidSubmission();
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java b/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java
new file mode 100644
index 0000000000000000000000000000000000000000..680736e1684c0c901b78510a4e9102ae84d34941
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/auth/OAuthService.java
@@ -0,0 +1,27 @@
+package de.fitko.fitconnect.api.services.auth;
+
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.submission.Submission;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.exceptions.AuthenticationException;
+
+/**
+ * A service that provides an interface to authenticate against the Fit-Connect API in order
+ * to send a {@link SubmissionSubmit} or to receive a {@link Submission}.
+ *
+ * @see <a href="https://docs.fitko.de/fit-connect/docs/getting-started/authentication">Fit-Connect documentation on authentication</a>
+ */
+public interface OAuthService {
+
+    /**
+     * Authenticates the sender/subscriber against the FIT-Connect API and retrieves an authentication token
+     *
+     * @param clientId     a unique identifier within the FIT-Connect platform environment
+     * @param clientSecret a secret that is only known by the application and the OAuth server
+     * @param scope        OAuth scope(s) that determine if a submission is accepted by the client
+     * @return {@link OAuthToken} that holds the access-token
+     *
+     * @throws {@link AuthenticationException} if an error occurred, including the original cause
+     */
+    OAuthToken authenticate(String clientId, String clientSecret, String... scope) throws AuthenticationException;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java b/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e66f3a1eb125cdab635d56efd43e51c82e392b2e
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/crypto/CryptoService.java
@@ -0,0 +1,61 @@
+package de.fitko.fitconnect.api.services.crypto;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.exceptions.DecryptionException;
+import de.fitko.fitconnect.api.exceptions.EncryptionException;
+
+/**
+ * A service that allows to encrypt and decrypt {@link Data} and {@link Attachment}s of a {@link SubmissionSubmit}
+ * via JSON-Web-Encryption.
+ *
+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc7516">JSON-Web-Encryption</a>
+ */
+public interface CryptoService {
+
+    /**
+     * Decrypts a JWE encrypted string with the given private key.
+     *
+     * @param privateKey    RSA private key for decryption of JWE
+     * @param encryptedData serialized encrypted JWE string
+     * @return decrypted JWE string
+     *
+     * @throws DecryptionException if the payload cannot be decrypted or there was an issue with the key
+     */
+    String decryptString(RSAKey privateKey, String encryptedData) throws DecryptionException;
+
+    /**
+     * Decrypts a JWE encrypted string with the given private key.
+     *
+     * @param privateKey    RSA private key for decryption of JWE
+     * @param encryptedData serialized encrypted JWE string
+     * @return decrypted JWE byte array
+     *
+     * @throws DecryptionException if the payload cannot be decrypted or there was an issue with the key
+     */
+    byte[] decryptBytes(RSAKey privateKey, String encryptedData) throws DecryptionException;
+
+    /**
+     * Encrypts a string with the given public key.
+     *
+     * @param publicKey RSA public key for encryption of string payload
+     * @param data      json or xml data that should be encrypted
+     * @return string serialization of the encrypted JWE object
+     *
+     * @throws EncryptionException if the payload cannot be encrypted or there was an issue with the key
+     */
+    String encryptString(RSAKey publicKey, String data) throws EncryptionException;
+
+    /**
+     * Encrypts a byte[] payload with the given public key.
+     *
+     * @param publicKey RSA public key the payload is encrypted with
+     * @param bytes     byte[] of the data that should be encrypted
+     * @return string serialization of the encrypted JWE object
+     *
+     * @throws EncryptionException if the payload cannot be encrypted or there was an issue with the key
+     */
+    String encryptBytes(RSAKey publicKey, byte[] bytes) throws EncryptionException;
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java b/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java
new file mode 100644
index 0000000000000000000000000000000000000000..ecd00d957d83c4fa3949208db4abbd8240f507c3
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/metadata/MetadataService.java
@@ -0,0 +1,34 @@
+package de.fitko.fitconnect.api.services.metadata;
+
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+
+import java.util.List;
+
+/**
+ * Provides the generation of correct and valid {@link Metadata} for a {@link SubmissionSubmit}
+ *
+ * @see
+ * <a href="https://docs.fitko.de/fit-connect/docs/metadataoverview">Metadata</a> and
+ * <a href="https://docs.fitko.de/fit-connect/docs/getting-started/submission/structure">Structure of a submission</a>
+ */
+public interface MetadataService {
+
+    /**
+     * Generates valid metadata for a submission including its JWE-encrypted data and attachments.
+     *
+     * @param data the payload (json or xml)
+     * @param attachments list of 0..n attachments as binary data
+     * @return {@link Metadata} with hashes
+     */
+    Metadata createMetadata(final Data data, final List<Attachment> attachments);
+
+    /**
+     * Uploads metadata to FIT-Connect API.
+     *
+     * @param metadata generated with {@link #createMetadata(Data, List)}
+     */
+    void uploadMetadata(final Metadata metadata);
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/validation/CertificateValidator.java b/api/src/main/java/de/fitko/fitconnect/api/services/validation/CertificateValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..004589da7112c2a71313620d66d05581e5fb414d
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/validation/CertificateValidator.java
@@ -0,0 +1,28 @@
+package de.fitko.fitconnect.api.services.validation;
+
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+
+/**
+ * Validator for publicKeys and certificate chains.
+ *
+ * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/encrypt/#certificateValidation">Certificate Validation</a>
+ */
+public interface CertificateValidator {
+
+    /**
+     * 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(final JWK publicKey);
+}
diff --git a/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java b/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..d61f1fbbd6bb5ec233cc536ff87f087be2b069be
--- /dev/null
+++ b/api/src/main/java/de/fitko/fitconnect/api/services/validation/MetadataValidator.java
@@ -0,0 +1,36 @@
+package de.fitko.fitconnect.api.services.validation;
+
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+
+/**
+ * A validator that ensures the integrity of the transferred {@link Metadata} of a {@link SubmissionSubmit}.
+ * It provides the validation of schema and hash values.
+ *
+ * @see <a href="https://docs.fitko.de/fit-connect/docs/sending/metadata#integrity">Metadata Integrity</a>
+ */
+public interface MetadataValidator {
+
+    /**
+     * Validates the metadata against a given schema.
+     *
+     * @param metadata the current metadata object that should be validated
+     * @param jsonSchema the schema the {@link Metadata} is validated against
+     *
+     * @return a {@link ValidationResult} with an optional error
+     */
+    ValidationResult validateMetadataSchema(final Metadata metadata, final String jsonSchema);
+
+    /**
+     * Checks if the message digest hashes of the metadata´s {@link Data} and {@link Attachment}s
+     * are correct.
+     *
+     * @param metadata the current {@link Metadata} object that should be validated
+     *
+     * @return a {@link ValidationResult} with an optional error
+     */
+    ValidationResult validateMetadataHashValues(final Metadata metadata);
+}
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000000000000000000000000000000000000..de4e4420438c488bb0a6aefcd553346f169aa7e2
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,367 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+        "https://checkstyle.org/dtds/configuration_1_3.dtd">
+
+<!--
+    Checkstyle configuration that checks the Google coding conventions from Google Java Style
+    that can be found at https://google.github.io/styleguide/javaguide.html
+
+    Checkstyle is very configurable. Be sure to read the documentation at
+    http://checkstyle.org (or in your downloaded distribution).
+
+    To completely disable a check, just comment it out or delete it from the file.
+    To suppress certain violations please review suppression filters.
+
+    Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
+ -->
+
+<module name = "Checker">
+    <property name="charset" value="UTF-8"/>
+
+    <property name="severity" value="warning"/>
+
+    <property name="fileExtensions" value="java, properties, xml"/>
+    <!-- Excludes all 'module-info.java' files              -->
+    <!-- See https://checkstyle.org/config_filefilters.html -->
+    <module name="BeforeExecutionExclusionFileFilter">
+        <property name="fileNamePattern" value="module\-info\.java$"/>
+    </module>
+    <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
+    <module name="SuppressionFilter">
+        <property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
+                  default="checkstyle-suppressions.xml" />
+        <property name="optional" value="true"/>
+    </module>
+
+    <!-- Checks for whitespace                               -->
+    <!-- See http://checkstyle.org/config_whitespace.html -->
+    <module name="FileTabCharacter">
+        <property name="eachLine" value="true"/>
+    </module>
+
+    <module name="LineLength">
+        <property name="fileExtensions" value="java"/>
+        <property name="max" value="100"/>
+        <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
+    </module>
+
+    <module name="TreeWalker">
+        <module name="OuterTypeFilename"/>
+        <module name="IllegalTokenText">
+            <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
+            <property name="format"
+                      value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
+            <property name="message"
+                      value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
+        </module>
+        <module name="AvoidEscapedUnicodeCharacters">
+            <property name="allowEscapesForControlCharacters" value="true"/>
+            <property name="allowByTailComment" value="true"/>
+            <property name="allowNonPrintableEscapes" value="true"/>
+        </module>
+        <module name="AvoidStarImport"/>
+        <module name="OneTopLevelClass"/>
+        <module name="NoLineWrap">
+            <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
+        </module>
+        <module name="EmptyBlock">
+            <property name="option" value="TEXT"/>
+            <property name="tokens"
+                      value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+        </module>
+        <module name="NeedBraces">
+            <property name="tokens"
+                      value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
+        </module>
+        <module name="LeftCurly">
+            <property name="tokens"
+                      value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
+                    INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
+                    LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
+                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
+                    OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
+        </module>
+        <module name="RightCurly">
+            <property name="id" value="RightCurlySame"/>
+            <property name="tokens"
+                      value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
+                    LITERAL_DO"/>
+        </module>
+        <module name="RightCurly">
+            <property name="id" value="RightCurlyAlone"/>
+            <property name="option" value="alone"/>
+            <property name="tokens"
+                      value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
+                    INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
+                    COMPACT_CTOR_DEF"/>
+        </module>
+        <module name="SuppressionXpathSingleFilter">
+            <!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
+            <property name="id" value="RightCurlyAlone"/>
+            <property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
+                                     or preceding-sibling::*[last()][self::LCURLY]]"/>
+        </module>
+        <module name="WhitespaceAfter">
+            <property name="tokens"
+                      value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN,
+                    LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS,
+                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA,
+                    LITERAL_YIELD, LITERAL_CASE"/>
+        </module>
+        <module name="WhitespaceAround">
+            <property name="allowEmptyConstructors" value="true"/>
+            <property name="allowEmptyLambdas" value="true"/>
+            <property name="allowEmptyMethods" value="true"/>
+            <property name="allowEmptyTypes" value="true"/>
+            <property name="allowEmptyLoops" value="true"/>
+            <property name="ignoreEnhancedForColon" value="false"/>
+            <property name="tokens"
+                      value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
+                    BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
+                    LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
+                    LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
+                    LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
+                    NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
+                    SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
+            <message key="ws.notFollowed"
+                     value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
+               may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
+            <message key="ws.notPreceded"
+                     value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+        </module>
+        <module name="OneStatementPerLine"/>
+        <module name="MultipleVariableDeclarations"/>
+        <module name="ArrayTypeStyle"/>
+        <module name="MissingSwitchDefault"/>
+        <module name="FallThrough"/>
+        <module name="UpperEll"/>
+        <module name="ModifierOrder"/>
+        <module name="EmptyLineSeparator">
+            <property name="tokens"
+                      value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                    STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
+                    COMPACT_CTOR_DEF"/>
+            <property name="allowNoEmptyLineBetweenFields" value="true"/>
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapDot"/>
+            <property name="tokens" value="DOT"/>
+            <property name="option" value="nl"/>
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapComma"/>
+            <property name="tokens" value="COMMA"/>
+            <property name="option" value="EOL"/>
+        </module>
+        <module name="SeparatorWrap">
+            <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
+            <property name="id" value="SeparatorWrapEllipsis"/>
+            <property name="tokens" value="ELLIPSIS"/>
+            <property name="option" value="EOL"/>
+        </module>
+        <module name="SeparatorWrap">
+            <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
+            <property name="id" value="SeparatorWrapArrayDeclarator"/>
+            <property name="tokens" value="ARRAY_DECLARATOR"/>
+            <property name="option" value="EOL"/>
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapMethodRef"/>
+            <property name="tokens" value="METHOD_REF"/>
+            <property name="option" value="nl"/>
+        </module>
+        <module name="PackageName">
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
+            <message key="name.invalidPattern"
+                     value="Package name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="TypeName">
+            <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                    ANNOTATION_DEF, RECORD_DEF"/>
+            <message key="name.invalidPattern"
+                     value="Type name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="MemberName">
+            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+            <message key="name.invalidPattern"
+                     value="Member name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="ParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Parameter name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="LambdaParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="CatchParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="LocalVariableName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Local variable name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="PatternVariableName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="ClassTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+            <message key="name.invalidPattern"
+                     value="Class type name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="RecordComponentName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
+            <message key="name.invalidPattern"
+                     value="Record component name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="RecordTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+            <message key="name.invalidPattern"
+                     value="Record type name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="MethodTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+            <message key="name.invalidPattern"
+                     value="Method type name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="InterfaceTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+            <message key="name.invalidPattern"
+                     value="Interface type name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="NoFinalizer"/>
+        <module name="GenericWhitespace">
+            <message key="ws.followed"
+                     value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+            <message key="ws.preceded"
+                     value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+            <message key="ws.illegalFollow"
+                     value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+            <message key="ws.notPreceded"
+                     value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+        </module>
+        <module name="Indentation">
+            <property name="basicOffset" value="2"/>
+            <property name="braceAdjustment" value="2"/>
+            <property name="caseIndent" value="2"/>
+            <property name="throwsIndent" value="4"/>
+            <property name="lineWrappingIndentation" value="4"/>
+            <property name="arrayInitIndent" value="2"/>
+        </module>
+        <module name="AbbreviationAsWordInName">
+            <property name="ignoreFinal" value="false"/>
+            <property name="allowedAbbreviationLength" value="0"/>
+            <property name="tokens"
+                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
+                    PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
+                    RECORD_COMPONENT_DEF"/>
+        </module>
+        <module name="NoWhitespaceBeforeCaseDefaultColon"/>
+        <module name="OverloadMethodsDeclarationOrder"/>
+        <module name="VariableDeclarationUsageDistance"/>
+        <module name="CustomImportOrder">
+            <property name="sortImportsInGroupAlphabetically" value="true"/>
+            <property name="separateLineBetweenGroups" value="true"/>
+            <property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
+            <property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
+        </module>
+        <module name="MethodParamPad">
+            <property name="tokens"
+                      value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
+                    SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
+        </module>
+        <module name="NoWhitespaceBefore">
+            <property name="tokens"
+                      value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
+                    LABELED_STAT, METHOD_REF"/>
+            <property name="allowLineBreaks" value="true"/>
+        </module>
+        <module name="ParenPad">
+            <property name="tokens"
+                      value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
+                    EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
+                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
+                    METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
+                    RECORD_DEF"/>
+        </module>
+        <module name="OperatorWrap">
+            <property name="option" value="NL"/>
+            <property name="tokens"
+                      value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
+                    LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
+                    TYPE_EXTENSION_AND "/>
+        </module>
+        <module name="AnnotationLocation">
+            <property name="id" value="AnnotationLocationMostCases"/>
+            <property name="tokens"
+                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
+                      RECORD_DEF, COMPACT_CTOR_DEF"/>
+        </module>
+        <module name="AnnotationLocation">
+            <property name="id" value="AnnotationLocationVariables"/>
+            <property name="tokens" value="VARIABLE_DEF"/>
+            <property name="allowSamelineMultipleAnnotations" value="true"/>
+        </module>
+        <module name="NonEmptyAtclauseDescription"/>
+        <module name="InvalidJavadocPosition"/>
+        <module name="JavadocTagContinuationIndentation"/>
+        <module name="SummaryJavadoc">
+            <property name="forbiddenSummaryFragments"
+                      value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
+        </module>
+        <module name="JavadocParagraph"/>
+        <module name="RequireEmptyLineBeforeBlockTagGroup"/>
+        <module name="AtclauseOrder">
+            <property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
+            <property name="target"
+                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
+        </module>
+        <module name="JavadocMethod">
+            <property name="accessModifiers" value="public"/>
+            <property name="allowMissingParamTags" value="true"/>
+            <property name="allowMissingReturnTag" value="true"/>
+            <property name="allowedAnnotations" value="Override, Test"/>
+            <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
+        </module>
+        <module name="MissingJavadocMethod">
+            <property name="scope" value="public"/>
+            <property name="minLineCount" value="2"/>
+            <property name="allowedAnnotations" value="Override, Test"/>
+            <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
+                                   COMPACT_CTOR_DEF"/>
+        </module>
+        <module name="MissingJavadocType">
+            <property name="scope" value="protected"/>
+            <property name="tokens"
+                      value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                      RECORD_DEF, ANNOTATION_DEF"/>
+            <property name="excludeScope" value="nothing"/>
+        </module>
+        <module name="MethodName">
+            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
+            <message key="name.invalidPattern"
+                     value="Method name ''{0}'' must match pattern ''{1}''."/>
+        </module>
+        <module name="SingleLineJavadoc"/>
+        <module name="EmptyCatchBlock">
+            <property name="exceptionVariableName" value="expected"/>
+        </module>
+        <module name="CommentsIndentation">
+            <property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
+        </module>
+        <!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
+        <module name="SuppressionXpathFilter">
+            <property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
+                      default="checkstyle-xpath-suppressions.xml" />
+            <property name="optional" value="true"/>
+        </module>
+    </module>
+</module>
diff --git a/client/README.md b/client/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6a64f768b6856bc6e7ef10711be18f58d70b229d
--- /dev/null
+++ b/client/README.md
@@ -0,0 +1,100 @@
+## Client Module
+
+### API Flow
+
+The ClientFactory provides fluent API clients both for **Sender** and **Subscriber**.
+
+As the flow chart below shows, the fluent client guides through all essential calls in order to hand in a correct 
+**Submission** as well as receive submissions on the subscriber side.
+
+#### Api client flow for sending a submission
+
+For the actual sender client those calls look like this:
+
+
+<table>
+<tr>
+<th>Workflow</th>
+<th>Java sample calls</th>
+</tr>
+<tr>
+<td>
+
+```mermaid
+
+flowchart TD
+
+A[Create Client] --> B(Provide DestinationID)
+B -->|next| C[Add Attachments]
+C -->|send| D[SubmissionForPickup]
+C -->|next| E[Add Data]
+E -->|send| D[SubmissionForPickup]
+```
+
+</td>
+<td>
+
+```java
+
+List<File> attachments...
+
+// Send without data
+        ClientFactory.senderClient()
+        .withDestination(UUID.randomUUID())
+        .withAttachments(attachments)
+        .submit();
+
+// Send with data
+        ClientFactory.senderClient()
+        .withDestination(UUID.randomUUID())
+        .withAttachments(attachments)
+        .withData("some json or xml content")
+        .submit();
+```
+
+</td>
+</tr>
+</table>
+
+#### Api client flow for subscribing to a submission
+
+<table>
+<tr>
+<th>Workflow</th>
+<th>Java sample calls</th>
+</tr>
+<tr>
+<td>
+
+```mermaid
+flowchart TD
+
+A[Create Client] --> B(Poll List ofAvailable Submissions)
+A[Create Client] --> C(Request Submission by ID)
+B -->|next| C[Request Submission by ID]
+C -->|get| D[Attachments]
+C -->|get| E[Metadata]
+C -->|get| F[Data]
+
+```
+
+</td>
+<td>
+
+```java
+
+    var client = ClientFactory.subscriberClient();
+
+    var submissions = client.getAvailableSubmissions(destinationId);
+    // filter submission list for requested one ...
+            
+    var submission = client.requestSubmission(submissionId)
+    submission.getAttachments();
+    submission.getMetadata();
+    submission.getData();
+```
+
+</td>
+</tr>
+</table>
+
diff --git a/client/pom.xml b/client/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..687f198f6c781d120b2b1aa888237a7be92cddfe
--- /dev/null
+++ b/client/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>de.fitko.fitconnect.sdk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>client</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>de.fitko.fitconnect.sdk</groupId>
+            <artifactId>api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>de.fitko.fitconnect.sdk</groupId>
+            <artifactId>dependency</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.beust</groupId>
+            <artifactId>jcommander</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/client/src/main/java/de/fitko/fitconnect/TestRunner.java b/client/src/main/java/de/fitko/fitconnect/TestRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..c530b55d6e41f8dd25fe6bca2e3befedb173df48
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/TestRunner.java
@@ -0,0 +1,52 @@
+package de.fitko.fitconnect;
+
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.client.factory.ClientFactory;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+public class TestRunner {
+
+    public static void main(final String[] args) {
+        senderSample();
+        subscriberSample();
+    }
+
+    private static void senderSample() {
+        final var destinationId = UUID.randomUUID();
+
+        // Without data
+        ClientFactory.senderClient()
+                .withDestination(destinationId)
+                .withAttachments(Collections.emptyList())
+                .submit();
+
+        // With data
+        ClientFactory.senderClient()
+                .withDestination(destinationId)
+                .withAttachments(Collections.emptyList())
+                .withData("some json or xml")
+                .submit();
+    }
+
+    private static void subscriberSample() {
+
+        final var destinationId = UUID.randomUUID();
+
+        final var client = ClientFactory.subscriberClient();
+        final var submissions = client.getAvailableSubmissions(destinationId);
+        final List<Attachment> attachments = client
+                .requestSubmission(getFirstSubmission(submissions))
+                .getAttachments();
+    }
+
+    private static UUID getFirstSubmission(final List<SubmissionForPickup> submissions) {
+        final Optional<SubmissionForPickup> submission = submissions.stream().findFirst();
+        return submission.isPresent() ? submission.get().getSubmissionId() : null;
+    }
+
+}
diff --git a/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java b/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..df433d8f4f922bf24f65378a384ce022cb720080
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/SenderClient.java
@@ -0,0 +1,195 @@
+package de.fitko.fitconnect.client;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.AttachmentUploadException;
+import de.fitko.fitconnect.api.exceptions.ClientNotAuthenticatedException;
+import de.fitko.fitconnect.api.exceptions.EncryptionException;
+import de.fitko.fitconnect.api.exceptions.InvalidPublicKeyException;
+import de.fitko.fitconnect.api.services.Sender;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * A fluent client for announcing and handing in a {@link SubmissionSubmit}
+ */
+public class SenderClient {
+
+    private SenderClient() {
+    }
+
+    public static WithDestination build(final Sender sender, final String clientId, final String secret) {
+        return new ClientBuilder(sender, clientId, secret);
+    }
+
+    public interface WithDestination {
+
+        /**
+         * Configures the client for the given destination and loads the public key
+         *
+         * @param destinationId unique identifier of the clients destination
+         * @return the upload step for attachments
+         */
+        WithAttachments withDestination(UUID destinationId);
+    }
+
+    public interface WithAttachments {
+
+        /**
+         * Sends the submission with a list of attachments
+         *
+         * @param attachments that are sent with the submission
+         * @return the step where additional data can be added to the submission
+         */
+        WithData withAttachments(List<File> attachments);
+    }
+
+    public interface WithData {
+
+        /**
+         * Data as string.
+         *
+         * @param data json or xml as string
+         * @return next step to submit the data
+         */
+        Submit withData(String data);
+
+        /**
+         * Data as byte array.
+         *
+         * @param data json or xml as byte array
+         * @return next step to submit the data
+         */
+        Submit withData(byte[] data);
+
+        /**
+         * Send submission to FIT-Connect API.
+         *
+         * @return {@link SubmissionForPickup} object of the handed in submission
+         */
+        SubmissionForPickup submit();
+    }
+
+    public interface Submit {
+        /**
+         * Send submission to FIT-Connect API.
+         *
+         * @return {@link SubmissionForPickup} object of the handed in submission
+         */
+        SubmissionForPickup submit();
+    }
+
+    public static class ClientBuilder implements WithDestination, WithAttachments, WithData, Submit {
+
+        private final Sender sender;
+        private final String clientId;
+        private final String secret;
+
+        private UUID destinationId;
+        private Data data;
+        private List<Attachment> attachments;
+        private RSAKey encryptionKey;
+        private UUID submissionId;
+
+        public ClientBuilder(final Sender sender, final String clientId, final String secret) {
+            this.sender = sender;
+            this.clientId = clientId;
+            this.secret = secret;
+            authenticate();
+        }
+
+        @Override
+        public WithAttachments withDestination(final UUID destinationId) {
+            final RSAKey encryptionKey = sender.getEncryptionKeyForDestination(destinationId);
+            final ValidationResult validationResult = sender.validatePublicKey(encryptionKey);
+            if (validationResult.hasError()) {
+                throw new InvalidPublicKeyException("Public encryption key is not valid", validationResult.getError());
+            }
+            this.encryptionKey = encryptionKey;
+            return this;
+        }
+
+        @Override
+        public Submit withData(final String data) {
+            return withData(data.getBytes(StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public Submit withData(final byte[] data) throws EncryptionException {
+            final var unencryptedData = Data.builder().build();
+            final Data encryptedData = sender.encryptSubmissionData(encryptionKey, unencryptedData);
+            this.data = encryptedData;
+            return this;
+        }
+
+        @Override
+        public WithData withAttachments(final List<File> attachmentFiles) throws AttachmentUploadException {
+            final List<Attachment> attachments = readFilesToAttachments(attachmentFiles);
+            final SubmissionForPickup submission = createSubmission(destinationId, attachments);
+            this.submissionId = submission.getSubmissionId();
+            final List<Attachment> encryptedAttachments = encryptAttachments(encryptionKey, attachments);
+            sender.uploadAttachments(submissionId, encryptedAttachments);
+            return this;
+        }
+
+        @Override
+        public SubmissionForPickup submit() {
+            return sender.sendSubmission(submissionId, getMetadata());
+        }
+
+        private Metadata getMetadata() {
+            if (this.data == null) {
+                return sender.createMetadata(attachments);
+            }
+            return sender.createMetadata(data, attachments);
+        }
+
+        private List<Attachment> encryptAttachments(final RSAKey encryptionKey, final List<Attachment> attachments) {
+            return attachments.stream().map(attachment -> encryptAttachment(encryptionKey, attachment)).collect(Collectors.toList());
+        }
+
+        private Attachment encryptAttachment(final RSAKey encryptionKey, final Attachment attachment) {
+            return sender.encryptAttachment(encryptionKey, attachment);
+        }
+
+        private SubmissionForPickup createSubmission(final UUID destinationId, final List<Attachment> attachments) {
+            final SubmissionSubmit request = SubmissionSubmit.builder()
+                    .destinationId(destinationId)
+                    .announcedAttachments(asListOfAttachmentsIds(attachments))
+                    .build();
+
+            return sender.createSubmission(request);
+        }
+
+        private List<UUID> asListOfAttachmentsIds(final List<Attachment> attachments) {
+            return attachments.stream().map(attachment -> attachment.getAttachmentId()).collect(Collectors.toList());
+        }
+
+        private void authenticate() {
+            final OAuthToken oAuthToken = sender.retrieveOAuthToken(clientId, secret);
+            if (oAuthToken == null) {
+                throw new ClientNotAuthenticatedException("Client is not authenticated, please authenticate first");
+            }
+        }
+
+        private List<Attachment> readFilesToAttachments(final List<File> attachmentFiles) {
+            return attachmentFiles.stream()
+                    .map(this::readFileToAttachment)
+                    .collect(Collectors.toList());
+        }
+
+        private Attachment readFileToAttachment(final File file) {
+            return sender.createAttachment(file);
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java b/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..c920fdade4722f740ba79c5e6d980b5fa387fd41
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/SubscriberClient.java
@@ -0,0 +1,177 @@
+package de.fitko.fitconnect.client;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.Submission;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.ClientNotAuthenticatedException;
+import de.fitko.fitconnect.api.exceptions.DecryptionException;
+import de.fitko.fitconnect.api.exceptions.ValidationException;
+import de.fitko.fitconnect.api.services.Subscriber;
+
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * A fluent client for handing in a subscription for a {@link Subscriber}
+ */
+public class SubscriberClient {
+
+    private SubscriberClient() {
+    }
+
+    public static RequestSubmission builder(final Subscriber subscriber, final String clientId, final String clientSecret, final String privateKey) {
+        return new ClientBuilder(subscriber, clientId, clientSecret, privateKey);
+    }
+
+    public interface RequestSubmission {
+
+        /**
+         * Loads a list of available {@link SubmissionForPickup} that were submitted to the {@link Subscriber}.
+         *
+         * @param destinationId unique identifier for a destination
+         * @param limit         number of submissions in result (max. is 500)
+         * @param offset        position in the dataset
+         *
+         * @return list of available submissions for pickup
+         */
+        List<SubmissionForPickup> getAvailableSubmissions(UUID destinationId, int offset, int limit);
+
+        /**
+         * Loads a list of available {@link SubmissionForPickup} that were submitted to the {@link Subscriber}.
+         *
+         * @param destinationId unique identifier for a destination
+         * @return list of available submissions for pickup
+         */
+        List<SubmissionForPickup> getAvailableSubmissions(UUID destinationId);
+
+        /**
+         * Loads a single {@link SubmissionForPickup} by id.
+         * @param submissionId unique identifier of a {@link SubmissionForPickup}
+         *
+         * @return next step to load available {@link Metadata}, {@link Data} and {@link Attachment}s
+         */
+        GetData requestSubmission(UUID submissionId);
+    }
+
+    public interface GetData {
+
+        /**
+         * Gets a {@link SubmissionForPickup}´s {@link Attachment}s
+         * @return list of {@link Attachment}s
+         */
+        List<Attachment> getAttachments();
+
+        /**
+         * Gets a {@link SubmissionForPickup}´s {@link Metadata}
+         * @return the {@link Metadata}
+         */
+        Metadata getMetadata();
+
+        /**
+         * Gets a {@link SubmissionForPickup}´s {@link Data}
+         * @return the {@link Data}
+         */
+        Data getData();
+    }
+
+    public static class ClientBuilder implements RequestSubmission, GetData {
+
+        private static final int DEFAULT_SUBMISSION_LIMIT = 100;
+
+        private final Subscriber subscriber;
+        private final String clientId;
+        private final String secret;
+        private final String privateKey;
+
+        public ClientBuilder(final Subscriber subscriber, final String clientId, final String clientSecret, final String privateKey) {
+            this.subscriber = subscriber;
+            this.clientId = clientId;
+            this.secret = clientSecret;
+            this.privateKey = privateKey;
+            authenticate();
+        }
+
+        @Override
+        public List<SubmissionForPickup> getAvailableSubmissions(final UUID destinationId) {
+            return getAvailableSubmissions(destinationId, 0, DEFAULT_SUBMISSION_LIMIT);
+        }
+
+        @Override
+        public List<SubmissionForPickup> getAvailableSubmissions(final UUID destinationId, final int offset, final int limit) {
+            return subscriber.pollAvailableSubmissions(destinationId, offset, limit);
+        }
+
+        @Override
+        public GetData requestSubmission(final UUID submissionId) {
+
+            final Submission submission = subscriber.getSubmission(submissionId);
+            final List<Attachment> attachments = subscriber.fetchAttachments(submissionId, submission.getAttachments());
+            final String encryptedData = submission.getEncryptedData();
+            final String encryptedMetadata = submission.getEncryptedMetadata();
+
+            final RSAKey decryptionKey = parseDecryptionKey(privateKey);
+
+            final byte[] decryptedSubmissionData = subscriber.decryptStringContent(decryptionKey, encryptedData);
+            final byte[] decryptedMetadata = subscriber.decryptStringContent(decryptionKey, encryptedMetadata);
+            // TODO generate objects metadata and data from byte[]
+
+            final List<ValidationResult> validationsResults = Collections.emptyList();
+            validationsResults.add(subscriber.validateEventLog(submission.getCaseId()));
+            validationsResults.add(subscriber.validateMetadata(getMetadata(), "schemaString"));
+            validationsResults.add(subscriber.validateData(getData()));
+            validationsResults.add(subscriber.validateAttachments(getAttachments()));
+
+            final Optional<ValidationResult> validationFailed = findFailedValidation(validationsResults);
+
+            if (validationFailed.isEmpty()) {
+                subscriber.confirmValidSubmission();
+                return this;
+            }
+
+            throw new ValidationException("Validation has errors, submission is not valid");
+        }
+
+        @Override
+        public List<Attachment> getAttachments() {
+            return null;
+        }
+
+        @Override
+        public Metadata getMetadata() {
+            return null;
+        }
+
+        @Override
+        public Data getData() {
+            return null;
+        }
+
+        private RSAKey parseDecryptionKey(final String privateKey) {
+            try {
+                return RSAKey.parse(privateKey);
+            } catch (final ParseException e) {
+                throw new DecryptionException("Key could not be parsed", e);
+            }
+        }
+
+        private Optional<ValidationResult> findFailedValidation(final List<ValidationResult> validationsResults) {
+            return validationsResults.stream().filter(result -> result.hasError()).findFirst();
+        }
+
+        private void authenticate() {
+            final OAuthToken oAuthToken = subscriber.retrieveOAuthToken(clientId, secret);
+            if (oAuthToken == null) {
+                throw new ClientNotAuthenticatedException("Client is not authenticated, please authenticate first");
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineArgs.java b/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineArgs.java
new file mode 100644
index 0000000000000000000000000000000000000000..47cf079798e4c85df988cb6cdb5dbd8652a295f5
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineArgs.java
@@ -0,0 +1,21 @@
+package de.fitko.fitconnect.client.cmd;
+
+import com.beust.jcommander.Parameter;
+
+public class CommandLineArgs {
+
+    @Parameter(
+            names = "--clientId",
+            description = "FitConnect OAuth clientId",
+            required = true
+    )
+    public String clientId;
+
+    @Parameter(
+            names = "--secret",
+            description = "FitConnect OAuth clientId",
+            required = true
+    )
+    public String secret;
+
+}
\ No newline at end of file
diff --git a/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineRunner.java b/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..daf87507ec5ee3bf4e7a97dbd4da9c79e45e1f71
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/cmd/CommandLineRunner.java
@@ -0,0 +1,24 @@
+package de.fitko.fitconnect.client.cmd;
+
+import com.beust.jcommander.JCommander;
+import de.fitko.fitconnect.client.SenderClient;
+
+public class CommandLineRunner {
+
+    public static void main(final String[] args) {
+
+        final CommandLineArgs cmdArgs = new CommandLineArgs();
+        final JCommander build = JCommander.newBuilder()
+                .addObject(cmdArgs)
+                .build();
+
+        build.parse(args);
+
+        final var clientId = cmdArgs.clientId;
+        final var secret = cmdArgs.secret;
+
+        // sample high -level- api calls to send a submission
+        //SenderClient.build(clientId, secret)
+
+    }
+}
diff --git a/client/src/main/java/de/fitko/fitconnect/client/di/Client.java b/client/src/main/java/de/fitko/fitconnect/client/di/Client.java
new file mode 100644
index 0000000000000000000000000000000000000000..95c3ef599e60827a9f2d4341dfcbfe37aa2c7b8e
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/di/Client.java
@@ -0,0 +1,36 @@
+package de.fitko.fitconnect.client.di;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.name.Names;
+import de.fitko.fitconnect.dependency.SdkModule;
+
+/**
+ * Abstract client that bootstraps the sdk´s dependency module and provides access to services and named properties.
+ */
+public abstract class Client {
+
+    private static final Injector injector = Guice.createInjector(new SdkModule());
+
+    /**
+     * Get implementation for a service interface.
+     *
+     * @param clazz class of interface
+     * @param <T> type of class interface
+     * @return implementation for the given service interface
+     */
+    static <T> T getService(final Class<T> clazz){
+        return injector.getInstance(clazz);
+    }
+
+    /**
+     * Get configuration property.
+     *
+     * @param propertyName name of the config property
+     * @return value of the named config property
+     */
+    static String getProperty(final String propertyName){
+        return injector.getInstance(Key.get(String.class, Names.named(propertyName)));
+    }
+}
diff --git a/client/src/main/java/de/fitko/fitconnect/client/di/FluentSenderClient.java b/client/src/main/java/de/fitko/fitconnect/client/di/FluentSenderClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fca87139719a1c4de25d2284288920569853eb6
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/di/FluentSenderClient.java
@@ -0,0 +1,200 @@
+package de.fitko.fitconnect.client.di;
+
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.ClientNotAuthenticatedException;
+import de.fitko.fitconnect.api.services.Sender;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * A fluent client for announcing and handing in a {@link SubmissionSubmit}
+ */
+public class FluentSenderClient extends Client {
+
+    private FluentSenderClient() {
+    }
+
+    public static WithDestination build() {
+        final Sender sender = getService(Sender.class);
+        final String clientId = getProperty("clientId");
+        final String secret = getProperty("clientSecret");
+        return new ClientBuilder(sender, clientId, secret);
+    }
+
+    public interface WithDestination {
+
+        /**
+         * Configures the client for the given destination and loads the public key
+         *
+         * @param destinationId the clients destination
+         * @return the upload step for attachments
+         */
+        WithAttachments withDestination(UUID destinationId);
+
+        SubmissionForPickup send(UUID destinationId, List<File> attachments, String data);
+
+        SubmissionForPickup send(UUID destinationId, List<File> attachments, byte[] data);
+
+        SubmissionForPickup send(UUID destinationId, List<File> attachments);
+    }
+
+    public interface WithAttachments {
+
+        /**
+         * Sends the submission with a list of attachments
+         *
+         * @param attachments that are sent with the submission
+         * @return the step where additional data can be added to the submission
+         */
+        WithData withAttachments(List<File> attachments);
+    }
+
+    public interface WithData {
+
+        Submit withData(String data);
+
+        Submit withData(byte[] data);
+
+        SubmissionForPickup submit();
+    }
+
+    public interface Submit {
+        SubmissionForPickup submit();
+    }
+
+    public static class ClientBuilder implements WithDestination, WithAttachments, WithData, Submit {
+
+        private final Sender sender;
+        private final String clientId;
+        private final String secret;
+
+        private UUID destinationId;
+        private Data data;
+        private List<Attachment> attachments;
+        private RSAKey encryptionKey;
+        private UUID submissionId;
+        private OAuthToken token;
+
+        public ClientBuilder(final Sender sender, final String clientId, final String secret) {
+            this.sender = sender;
+            this.clientId = clientId;
+            this.secret = secret;
+            authenticate();
+        }
+
+        @Override
+        public WithAttachments withDestination(final UUID destinationId) {
+            final RSAKey encryptionKey = sender.getEncryptionKeyForDestination(destinationId);
+            final ValidationResult validationResult = sender.validatePublicKey(encryptionKey);
+            if (validationResult.hasError()) {
+                throw new RuntimeException("Public encryption key is not valid", validationResult.getError());
+            }
+            this.encryptionKey = encryptionKey;
+            return this;
+        }
+
+        @Override
+        public SubmissionForPickup send(final UUID destinationId, final List<File> attachments, final String data) {
+            return this.withDestination(destinationId)
+                    .withAttachments(attachments)
+                    .withData(data)
+                    .submit();
+        }
+
+        @Override
+        public SubmissionForPickup send(final UUID destinationId, final List<File> attachments, final byte[] data) {
+            return this.withDestination(destinationId)
+                    .withAttachments(attachments)
+                    .withData(data)
+                    .submit();
+        }
+
+        @Override
+        public SubmissionForPickup send(final UUID destinationId, final List<File> attachments) {
+            return this.withDestination(destinationId)
+                    .withAttachments(attachments)
+                    .submit();
+        }
+
+        @Override
+        public Submit withData(final String data) {
+            return withData(data.getBytes(StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public Submit withData(final byte[] data) {
+            final var unencryptedData = Data.builder().build();
+            final Data encryptedData = sender.encryptSubmissionData(encryptionKey, unencryptedData);
+            this.data = encryptedData;
+            return this;
+        }
+
+        @Override
+        public WithData withAttachments(final List<File> attachmentFiles) {
+            final List<Attachment> attachments = readFilesToAttachments(attachmentFiles);
+            final SubmissionForPickup submission = createSubmission(destinationId, attachments);
+            this.submissionId = submission.getSubmissionId();
+            final List<Attachment> encryptedAttachments = encryptAttachments(encryptionKey, attachments);
+            sender.uploadAttachments(submissionId, encryptedAttachments);
+            return this;
+        }
+
+        @Override
+        public SubmissionForPickup submit() {
+            return sender.sendSubmission(submissionId, getMetadata());
+        }
+
+        private Metadata getMetadata() {
+            return this.data == null ? sender.createMetadata(attachments) : sender.createMetadata(data, attachments);
+        }
+
+        private List<Attachment> encryptAttachments(final RSAKey encryptionKey, final List<Attachment> attachments) {
+            return attachments.stream().map(attachment -> encryptAttachment(encryptionKey, attachment)).collect(Collectors.toList());
+        }
+
+        private Attachment encryptAttachment(final RSAKey encryptionKey, final Attachment attachment) {
+            return sender.encryptAttachment(encryptionKey, attachment);
+        }
+
+        private SubmissionForPickup createSubmission(final UUID destinationId, final List<Attachment> attachments) {
+            final SubmissionSubmit request = SubmissionSubmit.builder()
+                    .destinationId(destinationId)
+                    .announcedAttachments(asListOfAttachmentsIds(attachments))
+                    .build();
+
+            return sender.createSubmission(request);
+        }
+
+        private List<UUID> asListOfAttachmentsIds(final List<Attachment> attachments) {
+            return attachments.stream().map(attachment -> attachment.getAttachmentId()).collect(Collectors.toList());
+        }
+
+        private void authenticate() {
+            final OAuthToken oAuthToken = sender.retrieveOAuthToken(clientId, secret);
+            if (oAuthToken == null) {
+                throw new ClientNotAuthenticatedException("Could not authenticate client, please authenticate first");
+            }
+        }
+
+        private List<Attachment> readFilesToAttachments(final List<File> attachmentFiles) {
+            return attachmentFiles.stream()
+                    .map(this::readFileToAttachment)
+                    .collect(Collectors.toList());
+        }
+
+        private Attachment readFileToAttachment(final File file) {
+            return sender.createAttachment(file);
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/main/java/de/fitko/fitconnect/client/factory/ClientFactory.java b/client/src/main/java/de/fitko/fitconnect/client/factory/ClientFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..38b20a258c07ea4871509ff2a17f96c02e25b0f6
--- /dev/null
+++ b/client/src/main/java/de/fitko/fitconnect/client/factory/ClientFactory.java
@@ -0,0 +1,106 @@
+package de.fitko.fitconnect.client.factory;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
+import de.fitko.fitconnect.api.services.Sender;
+import de.fitko.fitconnect.api.services.Subscriber;
+import de.fitko.fitconnect.api.services.auth.OAuthService;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.api.services.metadata.MetadataService;
+import de.fitko.fitconnect.api.services.validation.CertificateValidator;
+import de.fitko.fitconnect.api.services.validation.MetadataValidator;
+import de.fitko.fitconnect.client.SenderClient;
+import de.fitko.fitconnect.client.SubscriberClient;
+import de.fitko.fitconnect.core.SubmissionSender;
+import de.fitko.fitconnect.core.SubmissionSubscriber;
+import de.fitko.fitconnect.core.auth.DefaultOAuthService;
+import de.fitko.fitconnect.core.crypto.JWECryptoService;
+import de.fitko.fitconnect.core.http.ProxyConfig;
+import de.fitko.fitconnect.core.metadata.MetadataUploadService;
+import de.fitko.fitconnect.core.validation.KeyValidator;
+import de.fitko.fitconnect.core.validation.MetadataSubmissionValidator;
+import de.fitko.fitconnect.dependency.ApplicationConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+
+/**
+ * Factory that constructs clients for {@link Sender} and {@link Subscriber}.
+ */
+public class ClientFactory {
+
+    private static final Logger logger = LoggerFactory.getLogger(ClientFactory.class);
+
+    public static final String CONFIG_FILENAME = "sdk.conf";
+    public static final String CONFIG_CONTEXT = "sdk";
+
+    private ClientFactory() {
+    }
+
+    /**
+     * Create a new {@link SenderClient} that is configured via {@link ApplicationConfig}.
+     *
+     * @return the sender client
+     */
+    public static SenderClient.WithDestination senderClient() {
+        final ApplicationConfig config = loadConfig();
+        final RestTemplate restTemplate = getRestTemplate(config);
+        final Sender sender = getSender(config, restTemplate);
+        return SenderClient.build(sender, config.getClientId(), config.getClientSecret());
+    }
+
+    /**
+     * Create a new {@link SubscriberClient} that is configured via {@link ApplicationConfig}.
+     *
+     * @return the sender client
+     */
+    public static SubscriberClient.RequestSubmission subscriberClient() {
+        final ApplicationConfig config = loadConfig();
+        final RestTemplate restTemplate = getRestTemplate(config);
+        final Subscriber subscriber = getSubscriber(config, restTemplate);
+        return SubscriberClient.builder(subscriber, config.getClientId(), config.getClientSecret(), config.getPrivateKey());
+    }
+
+    private static Subscriber getSubscriber(final ApplicationConfig config, final RestTemplate restTemplate) {
+        final OAuthService oAuthService = getOAuthService(config, restTemplate);
+        final CryptoService cryptoService = getCryptoService();
+        final MetadataValidator validator = new MetadataSubmissionValidator();
+        return new SubmissionSubscriber(oAuthService, cryptoService, validator);
+    }
+
+    private static Sender getSender(final ApplicationConfig config, final RestTemplate restTemplate) {
+        final OAuthService authService = getOAuthService(config, restTemplate);
+        final CryptoService cryptoService = getCryptoService();
+        final CertificateValidator validator = new KeyValidator();
+        final MetadataService metadataService = new MetadataUploadService(restTemplate);
+        return new SubmissionSender(authService, cryptoService, validator, metadataService);
+    }
+
+    private static CryptoService getCryptoService() {
+        return new JWECryptoService();
+    }
+
+    private static OAuthService getOAuthService(final ApplicationConfig config, final RestTemplate restTemplate) {
+        return new DefaultOAuthService(restTemplate, config.getActiveEnvironment().getAuthTokenUrl());
+    }
+
+    private static RestTemplate getRestTemplate(final ApplicationConfig config) {
+        final ProxyConfig proxyConfig = new ProxyConfig(config.getHttpProxyHost(), config.getHttpProxyPort());
+        return proxyConfig.proxyRestTemplate();
+    }
+
+    private static ApplicationConfig loadConfig() {
+        final Config configFile = ConfigFactory.parseFile(new File(CONFIG_FILENAME));
+        final Config sdkConfig = ConfigFactory.load(configFile).getConfig(CONFIG_CONTEXT);
+        final ApplicationConfig applicationConfig = ConfigBeanFactory.create(sdkConfig, ApplicationConfig.class);
+        logger.info("Using sdk environment config {} with {}", applicationConfig.getMode(), applicationConfig);
+        return applicationConfig;
+    }
+}
+
+
+
+
diff --git a/core/README.md b/core/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..51805e8db4a5d7ca67b5593237f2434eb7e86587
--- /dev/null
+++ b/core/README.md
@@ -0,0 +1,6 @@
+## Core Module
+
+The core module contains all interface implementations of the ``API`` module. A client should interact with the actual
+implementations only via the provided API service interfaces. It contains two main facades a clients can use to send and
+receive submissions.
+
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..06909aa70d5062ed7c21c3646f2a4d3e883ecf80
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>de.fitko.fitconnect.sdk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>core</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>de.fitko.fitconnect.sdk</groupId>
+            <artifactId>api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.erosb</groupId>
+            <artifactId>everit-json-schema</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+    </dependencies>
+
+
+
+</project>
\ No newline at end of file
diff --git a/core/src/main/java/de/fitko/fitconnect/core/SubmissionSender.java b/core/src/main/java/de/fitko/fitconnect/core/SubmissionSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..beb4937fd49b6a1d53c56449cb78aacaa3af7c61
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/SubmissionSender.java
@@ -0,0 +1,122 @@
+package de.fitko.fitconnect.core;
+
+import com.google.inject.Inject;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Hash__1;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.signature.Type;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionSubmit;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.exceptions.AttachmentCreationException;
+import de.fitko.fitconnect.api.services.Sender;
+import de.fitko.fitconnect.api.services.auth.OAuthService;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.api.services.metadata.MetadataService;
+import de.fitko.fitconnect.api.services.validation.CertificateValidator;
+import de.fitko.fitconnect.core.crypto.HashUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+public class SubmissionSender implements Sender {
+
+    private static final Logger logger = LoggerFactory.getLogger(SubmissionSender.class);
+
+    private final OAuthService authService;
+    private final CertificateValidator certificateValidator;
+    private final CryptoService cryptoService;
+    private final MetadataService metadataService;
+    private final HashUtil hashUtil = new HashUtil();
+
+    @Inject
+    public SubmissionSender(final OAuthService authService,
+                            final CryptoService encryptionService,
+                            final CertificateValidator certificateValidator,
+                            final MetadataService metadataService) {
+        this.authService = authService;
+        this.cryptoService = encryptionService;
+        this.certificateValidator = certificateValidator;
+        this.metadataService = metadataService;
+    }
+
+
+    @Override
+    public ValidationResult validatePublicKey(final JWK publicKey) {
+        return certificateValidator.validatePublicKey(publicKey);
+    }
+
+    @Override
+    public Data encryptSubmissionData(final RSAKey publicKey, final Data data) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public Attachment encryptAttachment(final RSAKey publicKey, final Attachment attachment) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public OAuthToken retrieveOAuthToken(final String clientId, final String clientSecret, final String... scope) {
+        final OAuthToken token = authService.authenticate(clientId, clientSecret, scope);
+        logger.debug("Successfully retrieved OAuth token: {}", token.getAccessToken());
+        return token;
+    }
+
+    @Override
+    public Metadata createMetadata(final Data data, final List<Attachment> attachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public Metadata createMetadata(final List<Attachment> attachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public SubmissionForPickup createSubmission(final SubmissionSubmit submission) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public SubmissionForPickup sendSubmission(final UUID submissionId, final Metadata encryptedMetadata) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public void uploadAttachments(final UUID submissionId, final List<Attachment> encryptedAttachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public Attachment createAttachment(final File file) {
+        try {
+            final String hashedFileContent = hashUtil.createHashFromFile(file);
+            final Hash__1 hash = new Hash__1();
+            hash.setType(Type.SHA_512);
+            hash.setContent(hashedFileContent);
+            return Attachment.builder()
+                    .attachmentId(UUID.randomUUID())
+                    .filename(file.getName())
+                    .hash(hash)
+                    .build();
+        } catch (final IOException e) {
+            logger.error("Attachment could not be read", e);
+            throw new AttachmentCreationException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public RSAKey getEncryptionKeyForDestination(final UUID destinationId) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/SubmissionSubscriber.java b/core/src/main/java/de/fitko/fitconnect/core/SubmissionSubscriber.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9368c1347197c6b4e68a1adc23a3d4ba2321e0d
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/SubmissionSubscriber.java
@@ -0,0 +1,91 @@
+package de.fitko.fitconnect.core;
+
+import com.google.inject.Inject;
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.domain.model.submission.Submission;
+import de.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.services.Subscriber;
+import de.fitko.fitconnect.api.services.auth.OAuthService;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.api.services.validation.MetadataValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.UUID;
+
+public class SubmissionSubscriber implements Subscriber {
+
+    private static final Logger logger = LoggerFactory.getLogger(SubmissionSubscriber.class);
+
+    private final OAuthService authService;
+    private final CryptoService cryptoService;
+    private final MetadataValidator metadataValidator;
+
+    @Inject
+    public SubmissionSubscriber(final OAuthService authService,
+                                final CryptoService cryptoService,
+                                final MetadataValidator metadataValidator) {
+        this.authService = authService;
+        this.cryptoService = cryptoService;
+        this.metadataValidator = metadataValidator;
+    }
+
+    @Override
+    public OAuthToken retrieveOAuthToken(final String clientId, final String clientSecret, final String... scope) {
+        final OAuthToken token = authService.authenticate(clientId, clientSecret, scope);
+        logger.debug("Successfully retrieved OAuth token: {}", token.getAccessToken());
+        return token;
+    }
+
+    @Override
+    public byte[] decryptStringContent(final RSAKey privateKey, final String encryptedContent) {
+        return cryptoService.decryptBytes(privateKey, encryptedContent);
+    }
+
+    @Override
+    public List<SubmissionForPickup> pollAvailableSubmissions(final UUID destinationId, final int limit, final int offset) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public Submission getSubmission(final UUID submissionId) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public List<Attachment> fetchAttachments(final UUID submissionId, final List<UUID> announcedAttachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public ValidationResult validateEventLog(final UUID caseId) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public ValidationResult validateMetadata(final Metadata metadata, final String schema) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public ValidationResult validateData(final Data data) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public ValidationResult validateAttachments(final List<Attachment> attachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public void confirmValidSubmission() {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/auth/DefaultOAuthService.java b/core/src/main/java/de/fitko/fitconnect/core/auth/DefaultOAuthService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b54e6408212f1366e859e92c510ceeedfc87abc
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/auth/DefaultOAuthService.java
@@ -0,0 +1,72 @@
+package de.fitko.fitconnect.core.auth;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.exceptions.AuthenticationException;
+import de.fitko.fitconnect.api.services.auth.OAuthService;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import static java.util.stream.Collectors.joining;
+
+public class DefaultOAuthService implements OAuthService {
+
+    private final RestTemplate restTemplate;
+    private final String tokenUrl;
+
+    @Inject
+    public DefaultOAuthService(final RestTemplate restTemplate, @Named("environment.authTokenUrl") final String tokenUrl) {
+        this.restTemplate = restTemplate;
+        this.tokenUrl = tokenUrl;
+    }
+
+    @Override
+    public OAuthToken authenticate(final String clientId, final String clientSecret, final String... scope) throws AuthenticationException {
+        final String requestBody = buildRequestBody(clientId, clientSecret, scope);
+        return performTokenRequest(requestBody);
+    }
+
+    private String buildRequestBody(final String clientId, final String clientSecret, final String... scope) {
+        final var data = new HashMap<String, String>() {{
+            put("grant_type", "client_credentials");
+            put("client_id", clientId);
+            put("client_secret", clientSecret);
+
+        }};
+
+        if (scope.length > 0) {
+            data.put("scope", String.join(",", scope));
+        }
+
+        return data.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(joining("&"));
+    }
+
+    private OAuthToken performTokenRequest(final String requestBody) throws AuthenticationException {
+        final HttpHeaders headers = getHeaders();
+        final HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
+        try {
+            return restTemplate.exchange(tokenUrl, HttpMethod.POST, entity, OAuthToken.class).getBody();
+        } catch (final RestClientException e) {
+            throw new AuthenticationException("could not retrieve OAuth token", e);
+        }
+    }
+
+    private HttpHeaders getHeaders() {
+        final var headers = new HttpHeaders();
+        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
+        return headers;
+    }
+
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/crypto/HashUtil.java b/core/src/main/java/de/fitko/fitconnect/core/crypto/HashUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..749f043afa960244fef6480d6eaa448516e61967
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/crypto/HashUtil.java
@@ -0,0 +1,57 @@
+package de.fitko.fitconnect.core.crypto;
+
+import de.fitko.fitconnect.api.exceptions.InitializationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class HashUtil {
+
+    private static final String DEFAULT_ALGORITHM = "SHA-512"; // Currently, only SHA-512 is supported.
+
+    private final MessageDigest messageDigest;
+
+    public HashUtil() {
+        try {
+            this.messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+        } catch (final NoSuchAlgorithmException e) {
+            throw new InitializationException(e.getMessage(), e);
+        }
+    }
+
+    public HashUtil(final MessageDigest messageDigest) {
+        this.messageDigest = messageDigest;
+    }
+
+    public byte[] createHash(final byte[] data) {
+        this.messageDigest.reset();
+        return this.messageDigest.digest(data);
+    }
+
+    public boolean verify(final byte[] originalHash, final byte[] data) {
+        final byte[] newHash = createHash(data);
+        return compareHashes(originalHash, newHash);
+    }
+
+    public String createHashFromFile(final File file) throws IOException {
+        try {
+            final byte[] rawData = Files.readAllBytes(Paths.get(file.getPath()));
+            return new String(rawData);
+        } catch (final IOException e) {
+            throw e;
+        }
+    }
+
+    private boolean compareHashes(final byte[] originalHash, final byte[] comparisonHash) {
+        int diff = originalHash.length ^ comparisonHash.length;
+        for (int i = 0; i < originalHash.length && i < comparisonHash.length; i++) {
+            diff |= originalHash[i] ^ comparisonHash[i];
+        }
+        return diff == 0;
+    }
+
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/crypto/JWECryptoService.java b/core/src/main/java/de/fitko/fitconnect/core/crypto/JWECryptoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..82cf7606eec4e5e6bce9246695f97815301d8142
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/crypto/JWECryptoService.java
@@ -0,0 +1,103 @@
+package de.fitko.fitconnect.core.crypto;
+
+import com.nimbusds.jose.*;
+import com.nimbusds.jose.crypto.RSADecrypter;
+import com.nimbusds.jose.crypto.RSAEncrypter;
+import com.nimbusds.jose.jwk.RSAKey;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.api.exceptions.DecryptionException;
+import de.fitko.fitconnect.api.exceptions.EncryptionException;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
+
+public class JWECryptoService implements CryptoService {
+
+    public static final JWEAlgorithm ALGORITHM = JWEAlgorithm.RSA_OAEP_256;
+    public static final EncryptionMethod ENCRYPTION_METHOD = EncryptionMethod.A256GCM;
+
+    @Override
+    public String decryptString(final RSAKey privateKey, final String encryptedData) throws DecryptionException {
+        return decrypt(privateKey, encryptedData).toString();
+    }
+
+    @Override
+    public byte[] decryptBytes(final RSAKey privateKey, final String encryptedData) throws DecryptionException {
+        return decrypt(privateKey, encryptedData).toBytes();
+    }
+
+    @Override
+    public String encryptString(final RSAKey publicKey, final String data) throws EncryptionException {
+        final Payload payload = new Payload(data);
+        return encrypt(publicKey, payload);
+    }
+
+    @Override
+    public String encryptBytes(final RSAKey publicKey, final byte[] bytes) throws EncryptionException {
+        final Payload payload = new Payload(bytes);
+        return encrypt(publicKey, payload);
+    }
+
+    private String encrypt(final RSAKey publicKey, final Payload payload) throws EncryptionException {
+        try {
+            final KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
+            keyGenerator.init(ENCRYPTION_METHOD.cekBitLength());
+            final SecretKey cek = keyGenerator.generateKey();
+            final String keyID = getIdFromPublicKey(publicKey);
+            return encryptPayload(publicKey, payload, cek, keyID);
+        } catch (final NoSuchAlgorithmException | JOSEException e) {
+            throw new EncryptionException(e.getMessage(), e);
+        }
+    }
+
+    private Payload decrypt(final RSAKey privateKey, final String encData) throws DecryptionException {
+        try {
+            final JWEObject jwe = JWEObject.parse(encData);
+            jwe.decrypt(new RSADecrypter(privateKey));
+            return jwe.getPayload();
+        } catch (final ParseException | JOSEException e) {
+            throw new DecryptionException(e.getMessage(), e);
+        }
+    }
+
+    private JWEObject getJWEObject(final String keyID, final Payload payload) {
+        return new JWEObject(getJWEHeader(keyID), payload);
+    }
+
+    private JWEHeader getJWEHeader(final String keyID) {
+        return new JWEHeader.Builder(ALGORITHM, ENCRYPTION_METHOD)
+                .compressionAlgorithm(CompressionAlgorithm.DEF)
+                .contentType("application/json")
+                .keyID(keyID)
+                .build();
+    }
+
+    private RSAEncrypter getEncrypter(final RSAPublicKey publicKey, final SecretKey cek) {
+        return new RSAEncrypter(publicKey, cek);
+    }
+
+    private String encryptPayload(final RSAKey publicKey, final Payload payload, final SecretKey cek, final String keyID) throws JOSEException, EncryptionException {
+        final JWEObject jwe = getJWEObject(keyID, payload);
+        jwe.encrypt(getEncrypter(publicKey.toRSAPublicKey(), cek));
+        checkIfJWEObjectIsEncrypted(jwe);
+        return jwe.serialize();
+    }
+
+    private void checkIfJWEObjectIsEncrypted(final JWEObject jwe) throws EncryptionException {
+        if (!jwe.getState().equals(JWEObject.State.ENCRYPTED)) {
+            throw new EncryptionException("JWE object is not encrypted");
+        }
+    }
+
+    private String getIdFromPublicKey(final RSAKey publicKey) throws EncryptionException {
+        final String keyID = publicKey.getKeyID();
+        if (keyID == null || keyID.isEmpty()) {
+            throw new EncryptionException("public key has no keyID");
+        }
+        return keyID;
+    }
+
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/http/ProxyConfig.java b/core/src/main/java/de/fitko/fitconnect/core/http/ProxyConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3e271979a245e5c473a924b51a1b91e2c064232
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/http/ProxyConfig.java
@@ -0,0 +1,45 @@
+package de.fitko.fitconnect.core.http;
+
+import com.google.common.base.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+public class ProxyConfig {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProxyConfig.class);
+
+    private final String host;
+    private final int port;
+
+    public ProxyConfig(String host, int port) {
+        this.host = host;
+        this.port = port;
+    }
+
+    public RestTemplate proxyRestTemplate() {
+        if (!hasProxySet()) {
+            logger.info("No proxy configured");
+            return new RestTemplate();
+        }
+
+        logger.info("Using proxy {}", this);
+        var requestFactory = new SimpleClientHttpRequestFactory();
+        var proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
+        requestFactory.setProxy(proxy);
+        return new ProxyRestTemplate(requestFactory);
+    }
+
+    public boolean hasProxySet() {
+        return !Strings.isNullOrEmpty(host) && port > 0;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ProxyConfig {host='%s', port=%d}", host, port);
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/de/fitko/fitconnect/core/http/ProxyRestTemplate.java b/core/src/main/java/de/fitko/fitconnect/core/http/ProxyRestTemplate.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea3aa13a05b47d48ee988adb52e7f25d1c9667e3
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/http/ProxyRestTemplate.java
@@ -0,0 +1,12 @@
+package de.fitko.fitconnect.core.http;
+
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+public class ProxyRestTemplate extends RestTemplate {
+
+    public ProxyRestTemplate(final ClientHttpRequestFactory requestFactory) {
+        super(requestFactory);
+        this.getMessageConverters().add(new X509CRLHttpMessageConverter());
+    }
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/http/X509CRLHttpMessageConverter.java b/core/src/main/java/de/fitko/fitconnect/core/http/X509CRLHttpMessageConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c677f3402f056c0ecdeba4f901b5adccce54837
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/http/X509CRLHttpMessageConverter.java
@@ -0,0 +1,42 @@
+package de.fitko.fitconnect.core.http;
+
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.AbstractHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.lang.NonNull;
+
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
+
+public class X509CRLHttpMessageConverter extends AbstractHttpMessageConverter<X509CRL> {
+
+    public X509CRLHttpMessageConverter() {
+        super(new MediaType("application", "pkix-crl"));
+    }
+
+    @Override
+    protected boolean supports(@NonNull Class<?> clazz) {
+        return X509CRL.class == clazz;
+    }
+
+    @Override
+    protected X509CRL readInternal(@NonNull Class<? extends X509CRL> clazz, @NonNull HttpInputMessage inputMessage)
+            throws HttpMessageNotReadableException {
+        try {
+            var cf = CertificateFactory.getInstance("X.509");
+            return (X509CRL) cf.generateCRL(inputMessage.getBody());
+        } catch (Exception e) {
+            throw new HttpMessageNotReadableException("CertificateFactory of type X.509 could not be created", inputMessage);
+        }
+    }
+
+    @Override
+    protected void writeInternal(@NonNull X509CRL x509CRL, @NonNull HttpOutputMessage outputMessage)
+            throws HttpMessageNotWritableException {
+        throw new HttpMessageNotWritableException("Writing X509CRL is not supported in ProxyRestTemplate");
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/de/fitko/fitconnect/core/metadata/MetadataUploadService.java b/core/src/main/java/de/fitko/fitconnect/core/metadata/MetadataUploadService.java
new file mode 100644
index 0000000000000000000000000000000000000000..aec1989dad84f0e24233bb257e6713dce6ee20ec
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/metadata/MetadataUploadService.java
@@ -0,0 +1,30 @@
+package de.fitko.fitconnect.core.metadata;
+
+import com.google.inject.Inject;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.domain.model.metadata.attachment.Attachment;
+import de.fitko.fitconnect.api.domain.model.metadata.data.Data;
+import de.fitko.fitconnect.api.services.metadata.MetadataService;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+
+public class MetadataUploadService implements MetadataService {
+
+    private final RestTemplate restTemplate;
+
+    @Inject
+    public MetadataUploadService(final RestTemplate restTemplate){
+        this.restTemplate = restTemplate;
+    }
+
+    @Override
+    public Metadata createMetadata(final Data data, final List<Attachment> attachments) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    @Override
+    public void uploadMetadata(final Metadata metadata) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/validation/KeyValidator.java b/core/src/main/java/de/fitko/fitconnect/core/validation/KeyValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..27b6e9decee40ee571f231dca4dbc019ce6332df
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/validation/KeyValidator.java
@@ -0,0 +1,13 @@
+package de.fitko.fitconnect.core.validation;
+
+import com.nimbusds.jose.jwk.JWK;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.api.services.validation.CertificateValidator;
+
+public class KeyValidator implements CertificateValidator {
+
+    @Override
+    public ValidationResult validatePublicKey(final JWK publicKey) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+}
diff --git a/core/src/main/java/de/fitko/fitconnect/core/validation/MetadataSubmissionValidator.java b/core/src/main/java/de/fitko/fitconnect/core/validation/MetadataSubmissionValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..88eca6cac1407d67a76bb257c2ad9fdc22560f8e
--- /dev/null
+++ b/core/src/main/java/de/fitko/fitconnect/core/validation/MetadataSubmissionValidator.java
@@ -0,0 +1,46 @@
+package de.fitko.fitconnect.core.validation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.fitko.fitconnect.api.domain.model.metadata.Metadata;
+import de.fitko.fitconnect.api.services.validation.MetadataValidator;
+import de.fitko.fitconnect.api.domain.validation.ValidationResult;
+import de.fitko.fitconnect.core.crypto.HashUtil;
+import org.everit.json.schema.Schema;
+import org.everit.json.schema.ValidationException;
+import org.everit.json.schema.loader.SchemaLoader;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+public class MetadataSubmissionValidator implements MetadataValidator {
+
+    public static final String DEFAULT_SCHEMA_PATH = "schemas/metadata.schema.json";
+    private static final HashUtil verifier = new HashUtil();
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Override
+    public ValidationResult validateMetadataSchema(Metadata metadata, String jsonSchema) {
+        final JSONObject schemaObject = new JSONObject(new JSONTokener(jsonSchema));
+        try {
+            JSONObject jsonSubject = toJsonObject(metadata);
+            Schema schema = SchemaLoader.load(schemaObject);
+            schema.validate(jsonSubject);
+        } catch (JsonProcessingException | ValidationException e) {
+            return ValidationResult.error(e);
+        }
+        return ValidationResult.ok();
+    }
+
+    @Override
+    public ValidationResult validateMetadataHashValues(Metadata metadata) {
+        return ValidationResult.ok();
+    }
+
+    private JSONObject toJsonObject(Metadata metadata) throws JsonProcessingException {
+        return new JSONObject(new JSONTokener(mapper.writeValueAsString(metadata)));
+    }
+
+    private String loadSchemaFile(final String jsonSchemaPath) {
+        return "";
+    }
+}
diff --git a/core/src/main/resources/schemas/metadata.schema.json b/core/src/main/resources/schemas/metadata.schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..997aeb08a5fe08d460c0e9efc23f4229931d5b97
--- /dev/null
+++ b/core/src/main/resources/schemas/metadata.schema.json
@@ -0,0 +1 @@
+{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json","type":"object","title":"Metadaten","description":"","required":["contentStructure"],"properties":{"contentStructure":{"description":"Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder Fachdaten.","type":"object","required":["attachments"],"properties":{"data":{"description":"Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.","type":"object","required":["hash","submissionSchema"],"properties":{"signature":{"type":"object","description":"Beschreibt das Signaturformt und Profile","examples":[],"properties":{"signatureFormat":{"type":"string","description":"Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt. Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML = XML-Signature, JSON = JSON Web Signature. ","enum":["cms","xml","pdf","asic","json"]},"eidasAdesProfile":{"type":"string","description":"Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal) gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).\n\nFür die Details zur Verwendung und Validierung von Profilen siehe auch https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification","enum":["http://uri.etsi.org/ades/191x2/level/baseline/B-B#","http://uri.etsi.org/ades/191x2/level/baseline/B-T#","http://uri.etsi.org/ades/191x2/level/baseline/B-LT#","http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#"]},"detachedSignature":{"type":"boolean","description":"Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des Fachdatensatzes bzw. der Anlage  (`false`) übertragen wird. Wenn der Wert `true` ist, dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen."},"content":{"type":"string","description":"Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.","pattern":"^[a-zA-Z0-9+/=]+|[a-zA-Z0-9_-]+$"}},"required":["signatureFormat","detachedSignature"]},"hash":{"title":"Hashwert","description":"Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).","type":"object","required":["type","content"],"properties":{"type":{"type":"string","description":"Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.","enum":["sha512"]},"content":{"type":"string","description":"Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.","pattern":"^[a-f0-9]{128}$"}}},"submissionSchema":{"title":"Fachdatenschema","description":"Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.","type":"object","required":["schemaUri","mimeType"],"properties":{"schemaUri":{"type":"string","format":"uri","description":"URI des Fachschemas. Wird hier eine URL verwendet, sollte das Schema unter der angegebenen URL abrufbar sein. Eine Verfügbarkeit des Schemas unter der angegebenen URL darf jedoch nicht vorausgesetzt werden."},"mimeType":{"type":"string","description":"Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B. XSD- oder JSON-Schema).","enum":["application/json","application/xml"]}}}}},"attachments":{"type":"array","items":{"type":"object","description":"Eine in der Einreichung enthaltene Anlage.","required":["hash","purpose","mimeType","attachmentId"],"properties":{"hash":{"title":"Hashwert","description":"Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).","type":"object","required":["type","content"],"properties":{"type":{"type":"string","description":"Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.","enum":["sha512"]},"content":{"type":"string","description":"Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.","pattern":"^[a-f0-9]{128}$"}}},"signature":{"$ref":"#/properties/contentStructure/properties/data/properties/signature"},"purpose":{"description":"Zweck/Art der Anlage\n- form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars\n- attachment: Anlage, die von einem Bürger hochgeladen wurde\n- report: Vom Onlinedienst, nachträglich erzeugte Unterlage","type":"string","enum":["form","attachment","report"]},"filename":{"type":"string","description":"Ursprünglicher Dateiname bei Erzeugung oder Upload"},"description":{"type":"string","description":"Optionale Beschreibung der Anlage"},"mimeType":{"type":"string","title":"MIME Type","description":"Internet Media Type gemäß RFC 2045, z. B. application/pdf.","examples":["application/xml"],"pattern":"^[-\\w.]+/[-\\w.+]+$"},"attachmentId":{"type":"string","description":"Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.","format":"uuid","minLength":32,"maxLength":36}}}}}},"publicServiceType":{"type":"object","title":"Verwaltungsleistung","description":"Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer anderen eindeutigen Schema-URN beschrieben werden.","properties":{"name":{"type":"string","description":"Name/Bezeichnung der Verwaltungsleistung"},"description":{"type":"string","description":"(Kurz-)Beschreibung der Verwaltungsleistung"},"identifier":{"title":"Leistungs-Identifikator","description":"URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier `urn:de:fim:leika:leistung:` vorangestellt werden.\n","type":"string","minLength":7,"maxLength":255,"pattern":"^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$"}},"required":["identifier"]},"authenticationInformation":{"description":"Eine Liste aller Identifikationsnachweise der Einreichung.","type":"array","minItems":1,"items":{"type":"object","description":"Eine Struktur, die einen Identifikationsnachweis beschreibt.","properties":{"type":{"description":"Definiert die Art des Identifikationsnachweises.","type":"string","enum":["identificationReport"]},"version":{"type":"string","pattern":"^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$","description":"semver kompatible Versionsangabe des genutzten Nachweistyps."},"content":{"type":"string","description":"Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.","pattern":"^[a-zA-Z0-9_\\-.]+$"}},"required":["type","version","content"]}},"paymentInformation":{"description":"Dieses Objekt enthält die Informationen vom Bezahldienst.","type":"object","required":["transactionReference","transactionId","paymentMethod","status"],"properties":{"transactionUrl":{"type":"string","format":"uri","minLength":1,"examples":["https://payment.bundesland.zzzz/api/v1/paymenttransaction/12002312/MELD-ANT-FORM-4711/9xxd-432x-6543-xfd6-gfdx-fd27"],"description":"Die Rest-URL der Payment Transaction für die Statusabfrage."},"transactionId":{"type":"string","minLength":1,"maxLength":36,"pattern":"^[\\w\\d-]+$","examples":["9xxd-432x-6543-xfd6-gfdx-fd27"],"description":"Eine vom Bezahldienst vergebene Transaktions-Id."},"transactionReference":{"type":"string","description":"Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen."},"transactionTimestamp":{"type":"string","format":"date-time","description":"Zeitstempel der erfolgreichen Durchführung der Bezahlung."},"paymentMethod":{"type":"string","enum":["GIROPAY","PAYDIRECT","CREDITCARD","PAYPAL","INVOICE","OTHER"],"examples":["CREDITCARD"],"description":"Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung vorhanden / befüllt."},"paymentMethodDetail":{"type":"string","minLength":1,"maxLength":36,"pattern":"^[\\w\\d-]+$","examples":["Visa"],"description":"Weitere Erläuterung zur gewählten Zahlart."},"status":{"type":"string","enum":["INITIAL","BOOKED","FAILED","CANCELED"],"description":"- INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine Wirkung erzeugt.\n- BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.\n- FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.\n- CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen."},"grossAmount":{"type":"number","minimum":0.01,"multipleOf":0.01,"description":"Bruttobetrag"}}},"replyChannel":{"type":"object","minProperties":1,"maxProperties":1,"properties":{"eMail":{"type":"object","properties":{"address":{"type":"string","format":"email"},"pgpPublicKey":{"type":"string","description":"Hilfe zur Erstellung gibt es in der Dokumentation unter https://docs.fitko.de/fit-connect/details/pgp-export","pattern":"^-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\\n"}},"required":["address"]},"deMail":{"type":"object","description":"Akkreditierte Anbieter siehe https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html","properties":{"address":{"type":"string","format":"email"}},"required":["address"]},"fink":{"type":"object","description":"Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)","properties":{"finkPostfachRef":{"type":"string","description":"FINK Postfachadresse","examples":["hh/by/12345"],"maxLength":150,"pattern":"^[-._a-z0-9~/]*$"},"host":{"type":"string","description":"URL des Servicekontos, in dem das Ziel-Postfach liegt","format":"uri","examples":["https://servicekonto1.example.com/"]}},"required":["finkPostfachRef"]},"elster":{"type":"object","description":"Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen","properties":{"accountId":{"type":"string","pattern":"^\\d{10}$"},"lieferTicket":{"type":"string"},"geschaeftszeichen":{"type":"string","maxLength":10}},"required":["accountId"]}}},"additionalReferenceInfo":{"type":"object","description":"Eine Struktur, um zusätzliche Informationen zu hinterlegen","properties":{"senderReference":{"type":"string","description":"Eine Referenz zum Vorgang im sendenden System, um bei Problemen und Rückfragen außerhalb von FIT-Connect den Vorgang im dortigen System schneller zu identifizieren."},"applicationDate":{"type":"string","format":"date","description":"Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der Einreichung des Antrags über FIT-Connect sein."}}}}}
\ No newline at end of file
diff --git a/core/src/test/java/de/fitko/fitconnect/core/auth/OAuthTokenIntegrationTest.java b/core/src/test/java/de/fitko/fitconnect/core/auth/OAuthTokenIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dacd8422c80c6dd2c5404d95cfceecfc1c5427a
--- /dev/null
+++ b/core/src/test/java/de/fitko/fitconnect/core/auth/OAuthTokenIntegrationTest.java
@@ -0,0 +1,38 @@
+package de.fitko.fitconnect.core.auth;
+
+import de.fitko.fitconnect.api.domain.auth.OAuthToken;
+import de.fitko.fitconnect.api.services.Sender;
+import de.fitko.fitconnect.core.SubmissionSender;
+import de.fitko.fitconnect.core.auth.DefaultOAuthService;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OAuthTokenIntegrationTest {
+
+    @Test
+    void retrieveAuthenticationToken() {
+
+        // Given
+        final var tokenUrl = "https://auth-testing.fit-connect.fitko.dev/token";
+        final var clientId = "781f6213-0f0f-4a79-9372-e7187ffda98b";
+        final var secret = "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o";
+        final var scope1 = "send:region:DE";
+        final var scope2 = "send:region:EN";
+
+        final var authService = new DefaultOAuthService(new RestTemplate(), tokenUrl);
+        final Sender sender = new SubmissionSender(authService, null, null, null);
+
+        // When
+        final OAuthToken token = sender.retrieveOAuthToken(clientId, secret, scope1, scope2);
+
+        // Then
+        assertNotNull(token);
+        assertNull(token.getError());
+        assertNotNull(token.getAccessToken());
+        assertEquals(1800, token.getExpiresIn());
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/de/fitko/fitconnect/core/crypto/HashUtilTest.java b/core/src/test/java/de/fitko/fitconnect/core/crypto/HashUtilTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4781501d48b434eebc99d5c5dbba82e418563a7e
--- /dev/null
+++ b/core/src/test/java/de/fitko/fitconnect/core/crypto/HashUtilTest.java
@@ -0,0 +1,30 @@
+package de.fitko.fitconnect.core.crypto;
+
+import de.fitko.fitconnect.core.crypto.HashUtil;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class HashUtilTest {
+
+    HashUtil underTest = new HashUtil();
+
+    @Test
+    public void testMessageDigestForDataAndAttachments() throws IOException {
+
+        // Given (sender creates hash of (Fachdaten) or attachments (Anhänge))
+        final byte[] exampleFileContent = getExampleFileContent("/example.pdf");
+        final byte[] hashFromSender = underTest.createHash(exampleFileContent);
+
+        // When (subscriber verifies hash of data or attachments)
+        final boolean verificationResult = underTest.verify(hashFromSender, exampleFileContent);
+        assertTrue(verificationResult);
+    }
+
+    private byte[] getExampleFileContent(final String path) throws IOException {
+        return HashUtil.class.getResourceAsStream(path).readAllBytes();
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/de/fitko/fitconnect/core/crypto/JWECryptoServiceTest.java b/core/src/test/java/de/fitko/fitconnect/core/crypto/JWECryptoServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..078212522b6b48d2a2ab01ff7e271cd5857fd5fe
--- /dev/null
+++ b/core/src/test/java/de/fitko/fitconnect/core/crypto/JWECryptoServiceTest.java
@@ -0,0 +1,57 @@
+package de.fitko.fitconnect.core.crypto;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.core.crypto.JWECryptoService;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class JWECryptoServiceTest {
+
+    final CryptoService underTest = new JWECryptoService();
+
+    @Test
+    void encryptData() throws Exception {
+
+        // Given
+        final RSAKey privateKey = generateRsaKey(4096);
+        final RSAKey publicKey = privateKey.toPublicJWK();
+
+        // When
+        final String encryptedData = underTest.encryptString(publicKey, "some foo");
+
+        // Then
+        assertNotNull(encryptedData);
+        assertNotEquals("some foo", encryptedData);
+    }
+
+    @Test
+    void decryptData() throws Exception {
+
+        // Given
+        final RSAKey privateKey = generateRsaKey(4096);
+        final RSAKey publicKey = privateKey.toPublicJWK();
+        final String encryptedData = underTest.encryptString(publicKey, "some foo");
+
+        // When
+        final String decrypted = underTest.decryptString(privateKey, encryptedData);
+
+        // Then
+        assertNotNull(decrypted);
+        assertEquals("some foo", decrypted);
+    }
+
+    public static RSAKey generateRsaKey(final int size) throws JOSEException {
+        return new RSAKeyGenerator(size)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID(UUID.randomUUID().toString())
+                .generate();
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/test/resources/example.pdf b/core/src/test/resources/example.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..92aa8424e344f57d9206aa85becd6198255a44b9
Binary files /dev/null and b/core/src/test/resources/example.pdf differ
diff --git a/dependency/README.md b/dependency/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9d0eccbe1455620e22dfdad784130fae0f2968c
--- /dev/null
+++ b/dependency/README.md
@@ -0,0 +1,46 @@
+## Dependency Module
+
+The DI module uses [Guice](https://github.com/google/guice/wiki/) as a dependency injection container of the SDK. All
+service interfaces that are bound to their implementations in one central location - the configure method.
+
+### Service Binding
+
+The ```SdkModule``` implements a guice Abstract Module and is configured with all necessary service binding. New
+Implementations or Providers can be registered here.
+
+```java
+
+@Override
+protected void configure(){
+
+        bindConfigProperties();
+
+        bind(CryptoService.class).to(JWECryptoService.class);
+        bind(CertificateValidator.class).to(KeyValidator.class);
+        bind(MetadataValidator.class).to(MetadataSubmissionValidator.class);
+        bind(MetadataService.class).to(MetadataUploadService.class);
+
+        bind(Sender.class).to(SubmissionSender.class);
+        bind(Subscriber.class).to(SubmissionSubscriber.class);
+        }
+```
+
+### Bootstrap and usage
+
+In order to initialize the container the sdk-module needs to be loaded:
+
+```java
+private static final Injector injector=Guice.createInjector(new SdkModule());
+```
+
+The actual injection can be done in three ways via @Inject:
+
+* Parameter Injection via @Injet on the field
+* Constructor Injection via @Inject on the constructor
+* Getting the Service directly from the DI Container, e.g. via ``injector.getService(Clazz.class)``
+
+### Application Properties
+
+The SDK can be externally configured via the config file ``/sdk.conf``. Those keys are loaded during guice
+initialization and can be accessed via the ``@Named`` annotation. See [Guice](https://github.com/google/guice/wiki/)
+for further information on the usage. 
diff --git a/dependency/pom.xml b/dependency/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9b050e048c976620bdaae137ee10a93807f0f540
--- /dev/null
+++ b/dependency/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>de.fitko.fitconnect.sdk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dependency</artifactId>
+
+  <dependencies>
+      <dependency>
+          <groupId>de.fitko.fitconnect.sdk</groupId>
+          <artifactId>api</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>de.fitko.fitconnect.sdk</groupId>
+          <artifactId>core</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>com.google.inject</groupId>
+          <artifactId>guice</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>com.typesafe</groupId>
+          <artifactId>config</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>org.projectlombok</groupId>
+          <artifactId>lombok</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+      </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/dependency/src/main/java/de/fitko/fitconnect/dependency/ApplicationConfig.java b/dependency/src/main/java/de/fitko/fitconnect/dependency/ApplicationConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcd890189d11270e3575dbbbf89c800a7d73fb2f
--- /dev/null
+++ b/dependency/src/main/java/de/fitko/fitconnect/dependency/ApplicationConfig.java
@@ -0,0 +1,70 @@
+package de.fitko.fitconnect.dependency;
+
+import de.fitko.fitconnect.core.SubmissionSender;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+@Getter
+@Setter
+@ToString
+public class ApplicationConfig {
+    
+    private String httpProxyHost;
+    private Integer httpProxyPort;
+
+    private String clientId;
+    private String clientSecret;
+    private String privateKey;
+
+    private Environments environments;
+
+    private String mode;
+
+    public Environment getActiveEnvironment() {
+        switch (mode) {
+            case "PROD":
+                return environments.prod;
+            case "TEST":
+                return environments.test;
+            default:
+                return environments.dev;
+        }
+    }
+
+    public Properties getAsProperties() {
+        final var props = new Properties();
+        props.put("httpProxyHost", getHttpProxyHost());
+        props.put("httpProxyPort", getHttpProxyPort());
+        props.put("clientId", clientId);
+        props.put("clientSecret", clientSecret);
+        props.put("privateKey", privateKey);
+        //props.put("environment", getActiveEnvironment(mode, environments));
+        return props;
+    }
+
+
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor
+    static public class Environments {
+        private Environment dev;
+        private Environment prod;
+        private Environment test;
+    }
+
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor
+    static public class Environment {
+        private String authTokenUrl;
+    }
+}
+
diff --git a/dependency/src/main/java/de/fitko/fitconnect/dependency/SdkModule.java b/dependency/src/main/java/de/fitko/fitconnect/dependency/SdkModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..972042ab9124619a43ffb667972cd6f6ace6a99f
--- /dev/null
+++ b/dependency/src/main/java/de/fitko/fitconnect/dependency/SdkModule.java
@@ -0,0 +1,64 @@
+package de.fitko.fitconnect.dependency;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigBeanFactory;
+import com.typesafe.config.ConfigFactory;
+import de.fitko.fitconnect.api.services.Sender;
+import de.fitko.fitconnect.api.services.Subscriber;
+import de.fitko.fitconnect.api.services.auth.OAuthService;
+import de.fitko.fitconnect.api.services.crypto.CryptoService;
+import de.fitko.fitconnect.api.services.metadata.MetadataService;
+import de.fitko.fitconnect.api.services.validation.CertificateValidator;
+import de.fitko.fitconnect.api.services.validation.MetadataValidator;
+import de.fitko.fitconnect.core.SubmissionSender;
+import de.fitko.fitconnect.core.SubmissionSubscriber;
+import de.fitko.fitconnect.core.auth.DefaultOAuthService;
+import de.fitko.fitconnect.core.crypto.JWECryptoService;
+import de.fitko.fitconnect.core.http.ProxyConfig;
+import de.fitko.fitconnect.core.metadata.MetadataUploadService;
+import de.fitko.fitconnect.core.validation.KeyValidator;
+import de.fitko.fitconnect.core.validation.MetadataSubmissionValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+
+public class SdkModule extends AbstractModule {
+
+    private static final Logger logger = LoggerFactory.getLogger(SdkModule.class);
+    public static final String CONFIG_NAME = "sdk.conf";
+
+    @Override
+    protected void configure() {
+
+        bindConfigProperties();
+
+        bind(CryptoService.class).to(JWECryptoService.class);
+        bind(CertificateValidator.class).to(KeyValidator.class);
+        bind(MetadataValidator.class).to(MetadataSubmissionValidator.class);
+        bind(MetadataService.class).to(MetadataUploadService.class);
+        bind(OAuthService.class).to(DefaultOAuthService.class);
+
+        bind(Sender.class).to(SubmissionSender.class);
+        bind(Subscriber.class).to(SubmissionSubscriber.class);
+    }
+
+    @Provides
+    @Singleton
+    RestTemplate provideRestTemplate(@Named("httpProxyHost") String proxyHost, @Named("httpProxyPort") int proxyPort) {
+        return new ProxyConfig(proxyHost, proxyPort).proxyRestTemplate();
+    }
+
+    private void bindConfigProperties() {
+        final Config conf = ConfigFactory.load( ConfigFactory.parseFile(new File(CONFIG_NAME))).getConfig("sdk");
+        final ApplicationConfig appConfig = ConfigBeanFactory.create(conf, ApplicationConfig.class);
+        Names.bindProperties(binder(), appConfig.getAsProperties());
+        logger.info("using sdk config: {}", appConfig);
+    }
+}
diff --git a/logback.xml b/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0d192e625eeb89cc8737c6fd0b6412233f16d3f4
--- /dev/null
+++ b/logback.xml
@@ -0,0 +1,10 @@
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <root level="debug">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>
\ No newline at end of file
diff --git a/open-api/README.md b/open-api/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ed8c51663b487526ac666ee185102aa6a9aa1ec1
--- /dev/null
+++ b/open-api/README.md
@@ -0,0 +1,47 @@
+### OpenAPI Model Generation
+
+This module generates model classes from the open-api specification of the submission api.
+
+Usage: ``mvn compile exec:java`` generates all models into the package ``de.fitko.fitconnect.api.domain.model.submission`` under
+**target/generated-sources/openapi/gen**.
+
+### Config
+
+The relevant config parameters are listed below.
+
+````xml
+
+<configuration>
+    <!-- open api spec url -->
+    <inputSpec>${submission-api.baseurl}/${submission-api.version}/${submission-api.spec}</inputSpec>
+
+    <!-- language generator -->
+    <generatorName>java</generatorName>
+
+    <!-- validation is currently disabled, otherwise the generation fails due to duplicate entries -->
+    <skipValidateSpec>true</skipValidateSpec>
+
+    <!-- use jackson annotations -->
+    <library>resttemplate</library>
+
+    <!-- target package name -->
+    <modelPackage>de.fitko.fitconnect.api.domain.model.submission</modelPackage>
+
+    <!-- target folder to write files into -->
+    <configOptions>
+        <sourceFolder>../../../src/main/java</sourceFolder>
+    </configOptions>
+</configuration>
+
+````
+
+### Draft/Todo PostProcessing
+
+The ``de.fitko.fitconnect.api.postprocessing.ModelClassPostProcessor`` is running in a maven execute step and 
+replaces all comments and unwanted annotations.
+
+Currently, there are some @Nullable and @NonNull annotations from ```javax.annotations``` that need to be replaced.
+
+In a further step the cleaned and post-processed model classes can be updated in the original ``api/domain/model``
+package of the ``api`` module via the maven resource plugin.
+
diff --git a/open-api/pom.xml b/open-api/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..849e77d139caf21a2ee754cf08be0dc570a826ca
--- /dev/null
+++ b/open-api/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>de.fitko.fitconnect.sdk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>open-api</artifactId>
+
+    <properties>
+        <submission-api.baseurl>https://schema.fitko.de/fit-connect/submission-api</submission-api.baseurl>
+        <submission-api.version>1.0.7</submission-api.version>
+        <submission-api.spec>submission-api.yaml</submission-api.spec>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.6.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>3.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <version>6.0.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${submission-api.baseurl}/${submission-api.version}/${submission-api.spec}
+                            </inputSpec>
+                            <generatorName>java</generatorName>
+                            <generateApis>false</generateApis>
+                            <skipValidateSpec>true</skipValidateSpec>
+                            <generateApis>false</generateApis>
+                            <generateModelDocumentation>false</generateModelDocumentation>
+                            <generateModelTests>false</generateModelTests>
+                            <generateSupportingFiles>false</generateSupportingFiles>
+                            <library>resttemplate</library>
+                            <modelPackage>de.fitko.fitconnect.api.domain.model.submission</modelPackage>
+                            <configOptions>
+                                <sourceFolder>gen</sourceFolder>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <configuration>
+                    <mainClass>de.fitko.fitconnect.api.postprocessing.ModelClassPostProcessor</mainClass>
+                    <workingDirectory>postprocessing</workingDirectory>
+                    <arguments>
+                        <argument>target/generated-sources/openapi/gen/de/fitko/fitconnect/api/domain/model/submission
+                        </argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>3.2.0</version>
+                <executions>
+                    <execution>
+                        <id>copy-resource-one</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.basedir}/../api/src/main/java/de/fitko/fitconnect/api/domain/generated
+                            </outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>
+                                        target/generated-sources/openapi/gen/de/fitko/fitconnect/api/domain/model/submission
+                                    </directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
\ No newline at end of file
diff --git a/open-api/src/main/java/de/fitko/fitconnect/api/postprocessing/ModelClassPostProcessor.java b/open-api/src/main/java/de/fitko/fitconnect/api/postprocessing/ModelClassPostProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea5203dba4095509b5591697c5ddda712d49dc32
--- /dev/null
+++ b/open-api/src/main/java/de/fitko/fitconnect/api/postprocessing/ModelClassPostProcessor.java
@@ -0,0 +1,91 @@
+package de.fitko.fitconnect.api.postprocessing;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ModelClassPostProcessor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ModelClassPostProcessor.class);
+
+    public static void main(final String[] args) {
+
+        final ModelClassPostProcessor postProcessor = new ModelClassPostProcessor();
+
+        final var dir = args[0]; // -modelPath
+
+        postProcessor.readModelsToStrings(dir)
+                .stream()
+                .map(ModelClassPostProcessor::replaceContent)
+                //.peek(pair -> logger.info(pair.content))
+                .forEach(ModelClassPostProcessor::writeFiles);
+    }
+
+    private static ModelPair replaceContent(final ModelPair pair) {
+        String content = pair.content;
+        // Remove all Comments
+        content = content.replaceAll("\\/\\*([\\S\\s]+?)\\*\\/", "");
+        content = content.replaceAll("(?s)/\\*.*?\\*/", "");
+        // Remove unwanted annos
+        content = content.replaceAll("@ApiModel([\\w]+)((.*))", "");
+        content = content.replaceAll("@ApiModelProperty([\\w]+)((.*))", "");
+        content = content.replaceAll("Generated([\\w]+)((.*))", "");
+        // Replace javax
+        //content = content.replaceAll("Nonnull([\\w]+)((.*))", "");
+        //content = content.replaceAll("(@\([\\w]+)\\((.*?)\\)(?:\s|$))", "");
+        return new ModelPair(pair.originalPath, content);
+    }
+
+    public Set<ModelPair> readModelsToStrings(final String dir) {
+        return Stream.of(new File(dir).listFiles())
+                .filter(file -> !file.isDirectory())
+                .peek(file -> logger.info("Postprocessing model {}", file.getName()))
+                .map(file -> new ModelPair(file.toPath(), getFileAsString(file)))
+                .collect(Collectors.toSet());
+    }
+
+    private String getFileAsString(final File file) {
+        try {
+            return Files.readString(file.toPath(), StandardCharsets.UTF_8);
+        } catch (final IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void writeFiles(final ModelPair modelPair) {
+        try {
+            final File f = new File(modelPair.originalPath.toFile().getAbsolutePath());
+            final FileWriter fw = new FileWriter( f, false);
+            fw.write(modelPair.content);
+            fw.flush();
+            fw.close();
+        } catch (final IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    private static class ModelPair {
+        final Path originalPath;
+        final String content;
+
+        ModelPair(final Path originalPath, final String content) {
+            this.originalPath = originalPath;
+            this.content = content;
+        }
+
+        @Override
+        public String toString() {
+            return content;
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index 59250755823f6b232861b688175042f452085674..38891393345990d101e0e23fc32eef397babaf22 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,13 +4,163 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
-    <groupId>de.fitconnect.sdk</groupId>
+    <groupId>de.fitko.fitconnect.sdk</groupId>
     <artifactId>sdk-java</artifactId>
+    <packaging>pom</packaging>
     <version>1.0-SNAPSHOT</version>
 
     <properties>
-        <maven.compiler.source>17</maven.compiler.source>
-        <maven.compiler.target>17</maven.compiler.target>
+        <java.version>11</java.version>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+
+        <!-- 3rd party dependencies -->
+        <nimbus.version>9.19</nimbus.version>
+        <jackson.version>2.13.3</jackson.version>
+        <json-schema.version>1.14.1</json-schema.version>
+        <lombock.version>1.18.24</lombock.version>
+        <typesafe-config.version>1.4.2</typesafe-config.version>
+        <spring-web.version>5.3.21</spring-web.version>
+        <guice.version>5.1.0</guice.version>
+        <logback.version>1.2.11</logback.version>
+        <jcommander.version>1.82</jcommander.version>
+
+        <junit.version>5.8.2</junit.version>
+        <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
+        <maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
+
     </properties>
 
+    <modules>
+        <module>api</module>
+        <module>core</module>
+        <module>client</module>
+        <module>dependency</module>
+        <module>open-api</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- Module Dependencies -->
+            <dependency>
+                <groupId>de.fitko.fitconnect.sdk</groupId>
+                <artifactId>api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>de.fitko.fitconnect.sdk</groupId>
+                <artifactId>core</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>de.fitko.fitconnect.sdk</groupId>
+                <artifactId>dependency</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-annotations</artifactId>
+                <version>${jackson.version}</version>
+                <scope>compile</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>com.nimbusds</groupId>
+                <artifactId>nimbus-jose-jwt</artifactId>
+                <version>${nimbus.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.erosb</groupId>
+                <artifactId>everit-json-schema</artifactId>
+                <version>${json-schema.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-web</artifactId>
+                <version>${spring-web.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.google.inject</groupId>
+                <artifactId>guice</artifactId>
+                <version>${guice.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>${logback.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.beust</groupId>
+                <artifactId>jcommander</artifactId>
+                <version>${jcommander.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.typesafe</groupId>
+                <artifactId>config</artifactId>
+                <version>${typesafe-config.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombock.version}</version>
+                <scope>provided</scope>
+            </dependency>
+
+            <!-- Testing -->
+            <dependency>
+                <groupId>org.junit.jupiter</groupId>
+                <artifactId>junit-jupiter</artifactId>
+                <version>${junit.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.junit</groupId>
+                <artifactId>junit-bom</artifactId>
+                <version>${junit.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>${maven-checkstyle-plugin.version}</version>
+                <configuration>
+                    <configLocation>checkstyle.xml</configLocation>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
\ No newline at end of file
diff --git a/sdk.conf b/sdk.conf
new file mode 100644
index 0000000000000000000000000000000000000000..853cc4fafc76aefbcfed8c2b0bfec83cfef5f695
--- /dev/null
+++ b/sdk.conf
@@ -0,0 +1,32 @@
+# SKD application properties and global configurations
+sdk {
+
+  # Credentials to authenticate via OAuth
+  clientId: "781f6213-0f0f-4a79-9372-e7187ffda98b"
+  clientSecret: "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o"
+  # Private key
+  # find location (ENV-var ?)
+  # and suitable format (PEM-Container ?)
+  privateKey: "$ref"
+
+  # Proxy config for http api calls
+  httpProxyHost: ""
+  httpProxyPort: 0
+
+  # Mode to switch between the active environments DEV, PROD or TEST
+  mode: "DEV"
+
+  # Configured environments for all api-urls
+  environments {
+    dev {
+        authTokenUrl: "https://auth-testing.fit-connect.fitko.dev/token"
+        # submissionApiUrls: ["http://test.url1", "http://test.url2", "http://test.url3"]
+    }
+    prod {
+         authTokenUrl: "https://auth-prod.fit-connect.fitko.dev/token"
+    }
+    test {
+        authTokenUrl: "https://auth-test.fit-connect.fitko.dev/token"
+    }
+  }
+}