diff --git a/.gitignore b/.gitignore
index 3585f2b73ecf1c9ddc9c46a21d91f18dbe79b937..8a5e7f13989c72b4b86131b33e8fe02c662b85ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,10 @@
 *private.env.json
+**/*.secret.json
+**/encryptionKeys
+**/assets/attachment.pdf
+**/**notes.md
+
+private_notes/
 
 ### VisualStudioCode template
 .vscode/*
@@ -10,6 +16,10 @@
 
 # Local History for Visual Studio Code
 .history/
+.DS_Store
+
+**.nupkg
+
 
 ### JetBrains template
 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
@@ -476,3 +486,4 @@ Network Trash Folder
 Temporary Items
 .apdisk
 
+deploy.sh
diff --git a/.idea/.idea.FitConnect/.idea/codeStyles/codeStyleConfig.xml b/.idea/.idea.FitConnect/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a55e7a179bde3e4e772c29c0c85e53354aa54618
--- /dev/null
+++ b/.idea/.idea.FitConnect/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/BasicUnitTest/BasicUnitTest.csproj b/BasicUnitTest/BasicUnitTest.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..87a4ad558fb5cab8be7c9ea8ad23e2cdac2ea72e
--- /dev/null
+++ b/BasicUnitTest/BasicUnitTest.csproj
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+
+        <RootNamespace>FluentApiTest</RootNamespace>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="FluentAssertions" Version="6.7.0" />
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+        <PackageReference Include="Moq" Version="4.18.1" />
+        <PackageReference Include="NUnit" Version="3.13.2" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
+        <PackageReference Include="coverlet.collector" Version="3.1.0" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\FitConnect\FitConnect.csproj" />
+        <ProjectReference Include="..\MockContainer\MockContainer.csproj" />
+    </ItemGroup>
+
+</Project>
diff --git a/BasicUnitTest/FluentSenderTests.cs b/BasicUnitTest/FluentSenderTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0951d3b8279130ae2069fe2e2fcee6c7a3ec1c16
--- /dev/null
+++ b/BasicUnitTest/FluentSenderTests.cs
@@ -0,0 +1,100 @@
+using System;
+using Autofac;
+using FitConnect;
+using FitConnect.Models;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+using NUnit.Framework;
+
+namespace FluentApiTest;
+
+public class FluentSenderTests {
+    private IContainer _container = null!;
+    protected string clientId = null!;
+    protected string clientSecret = null!;
+    private ILogger? logger;
+    private string leikaKey = null!;
+
+    [OneTimeSetUp]
+    public void OneTimeSetup() {
+        _container = Container.Create();
+        logger = _container.Resolve<ILogger>();
+        var settings = _container.Resolve<MockSettings>();
+        clientId = settings.SenderClientId;
+        clientSecret = settings.SenderClientSecret;
+        leikaKey = settings.LeikaKey;
+    }
+
+    [SetUp]
+    public void Setup() {
+    }
+
+    [Test]
+    public void FluentSender_ShouldNotThrowAnError_ArgumentExceptionLeikaKeyInvalid() {
+        Assert.Throws<ArgumentException>(() => {
+            new Sender(FitConnectEnvironment.Testing,
+                    clientId, clientSecret,
+                    Container.Create())
+                .WithDestination(Guid.NewGuid().ToString())
+                .WithServiceType("", "")
+                .WithAttachments(Array.Empty<Attachment>())
+                .WithData("")
+                .Submit();
+        })!.Message.Should().Be("Invalid leika key");
+    }
+
+    [Test]
+    public void FluentSender_ShouldNotThrowAnError_MockingServicesWithData() {
+        new Sender(FitConnectEnvironment.Testing,
+                clientId, clientSecret,
+                Container.Create())
+            .WithDestination(Guid.NewGuid().ToString())
+            .WithServiceType("", leikaKey)
+            .WithAttachments(Array.Empty<Attachment>())
+            .WithData("")
+            .Submit();
+    }
+
+    [Test]
+    public void FluentSender_ShouldNotThrowAnError_MockingServicesWithoutData() {
+        new Sender(FitConnectEnvironment.Testing,
+                clientId, clientSecret,
+                Container.Create())
+            .WithDestination(Guid.NewGuid().ToString())
+            .WithServiceType("", leikaKey)
+            .WithAttachments(Array.Empty<Attachment>())
+            .Submit();
+    }
+
+    [Test]
+    public void VerifyMetaData_ValidData_Fine() {
+        // Arrange
+        var submission = new Submission();
+        submission.ServiceType.Identifier = leikaKey;
+
+        // Act
+        var metadata = Sender.CreateMetadata(submission);
+
+        // Assert
+        var errors = Subscriber.VerifyMetadata(metadata);
+        foreach (var error in errors) Console.WriteLine(error.ToString());
+
+        errors.Count.Should().Be(0);
+    }
+
+    [Test]
+    public void VerifyMetaData_MissingLeikaKey_ThorwsAnError() {
+        // Arrange
+        var submission = new Submission();
+
+        // Act
+        var metadata = Sender.CreateMetadata(submission);
+
+        // Assert
+        var errors = Subscriber.VerifyMetadata(metadata);
+        foreach (var error in errors) Console.WriteLine(error.ToString());
+
+        errors.Count.Should().Be(1);
+    }
+}
diff --git a/BasicUnitTest/FluentSubscriberReceiveTests.cs b/BasicUnitTest/FluentSubscriberReceiveTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6ab27366f830c539631570796c1088c38bc33373
--- /dev/null
+++ b/BasicUnitTest/FluentSubscriberReceiveTests.cs
@@ -0,0 +1,41 @@
+using Autofac;
+using FitConnect;
+using FitConnect.Models;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+using NUnit.Framework;
+
+namespace FluentApiTest;
+
+public class FluentSubscriberReceiveTests {
+    private readonly string clientId = "clientId";
+    private readonly string clientSecret = "clientSecret";
+    private IContainer _container = null!;
+    private ILogger _logger = null!;
+    private MockSettings _mockSettings = null!;
+
+    [OneTimeSetUp]
+    public void OneTimeSetup() {
+        _container = Container.Create();
+        _logger = _container.Resolve<ILogger>();
+        _mockSettings = _container.Resolve<MockSettings>();
+    }
+
+    [SetUp]
+    public void SetUp() {
+    }
+
+    [Test]
+    [Ignore("Missing encrypted data")]
+    public void GetSubmission() {
+        var fluentSubscriber =
+            new Subscriber(FitConnectEnvironment.Testing, "", "", "", "", "", "", logger: null);
+
+        var submissions = fluentSubscriber
+            .GetAvailableSubmissions();
+
+        var attachments = fluentSubscriber
+            .RequestSubmission("submissionId")
+            .GetAttachments();
+    }
+}
diff --git a/BasicUnitTest/SecurityEventTokenTests.cs b/BasicUnitTest/SecurityEventTokenTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a2f752acaf07d45c9442e0f8e04e5e4e60b5e1f7
--- /dev/null
+++ b/BasicUnitTest/SecurityEventTokenTests.cs
@@ -0,0 +1,149 @@
+using System;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Models;
+using FitConnect.Models.v1.Api;
+using FluentAssertions;
+using NUnit.Framework;
+using SecurityEventToken = FitConnect.Models.SecurityEventToken;
+
+namespace FluentApiTest;
+
+[TestFixture]
+public class SecurityEventTokenTests {
+    private const string rejectSubmission =
+        SecurityEventToken.RejectSubmissionSchema;
+
+    private FitEncryption _encryption = null!;
+
+    private const string acceptSubmission =
+        SecurityEventToken.AcceptSubmissionSchema;
+
+    [SetUp]
+    public void Setup() {
+        var container = MockContainer.Container.Create();
+        _encryption = new FitEncryption(container.Resolve<KeySet>(), null);
+    }
+
+    [Test]
+    public void CreateJwt_AcceptSubmission() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            acceptSubmission,
+            null
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(acceptSubmission);
+        decoded.EventType.Should().Be(EventType.Accept);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithEncryptionIssue() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.EncryptionIssue }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithMissingSchema() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.MissingSchema }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithSchemaViolation() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.SchemaViolation }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithSyntaxViolation() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.SyntaxViolation }
+        );
+        Console.WriteLine(token);
+
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithUnsupportedSchema() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.UnsupportedSchema }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithIncorrectAuthenticationTag() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { Problems.IncorrectAuthenticationTag }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+
+    [Test]
+    public void CreateJwt_Reject_WithCustomProblem() {
+        var token = _encryption.CreateSecurityEventToken(Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            rejectSubmission,
+            new[] { new Problems() { Description = "A real big issue" } }
+        );
+        Console.WriteLine(token);
+        var decoded = new FitConnect.Models.SecurityEventToken(token);
+        decoded.Event?.Type.Should()
+            .Be(rejectSubmission);
+        decoded.EventType.Should().Be(EventType.Reject);
+    }
+}
diff --git a/DummyClient/.dockerignore b/DemoRunner/.dockerignore
similarity index 100%
rename from DummyClient/.dockerignore
rename to DemoRunner/.dockerignore
diff --git a/DemoRunner/.gitignore b/DemoRunner/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..16bf3adb5b4003c3a628bdea461c41238d3d1b74
--- /dev/null
+++ b/DemoRunner/.gitignore
@@ -0,0 +1 @@
+appsettings.json
diff --git a/DemoRunner/DemoRunner.csproj b/DemoRunner/DemoRunner.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..1e670320d5da6e923c6e02273e721b38128fafd4
--- /dev/null
+++ b/DemoRunner/DemoRunner.csproj
@@ -0,0 +1,48 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
+        <IsPackable>false</IsPackable>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="FitConnect" Version="0.0.1-beta.2"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <None Update="appsettings.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="Test.pdf">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\set-public-keys.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\privateKey_signing.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\publicKey_encryption.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\privateKey_decryption.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\publicKey_signature_verification.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\FitConnect\FitConnect.csproj"/>
+    </ItemGroup>
+
+</Project>
diff --git a/DemoRunner/Dockerfile b/DemoRunner/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..69fa8ac7f6fb0b815f0201eda443c5bae1dc0fcb
--- /dev/null
+++ b/DemoRunner/Dockerfile
@@ -0,0 +1,18 @@
+FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+WORKDIR /src
+COPY ["DemoRunner/DemoRunner.csproj", "DemoRunner/"]
+RUN dotnet restore "DemoRunner/DemoRunner.csproj"
+COPY . .
+WORKDIR "/src/DemoRunner"
+RUN dotnet build "DemoRunner.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "DemoRunner.csproj" -c Release -o /app/publish
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "DemoRunner.dll"]
diff --git a/DemoRunner/OutputHelper.cs b/DemoRunner/OutputHelper.cs
new file mode 100644
index 0000000000000000000000000000000000000000..99de311f3bbd552e1567e15883f025288e8efda8
--- /dev/null
+++ b/DemoRunner/OutputHelper.cs
@@ -0,0 +1,38 @@
+namespace DemoRunner;
+
+public static class OutputHelper {
+    #region Headline Output
+
+// @formatter:off
+public static void PrintFitConnect() {
+    Console.WriteLine(@"    ______ ____ ______      ______                                  __  ");
+    Console.WriteLine(@"   / ____//  _//_  __/     / ____/____   ____   ____   ___   _____ / /_ ");
+    Console.WriteLine(@"  / /_    / /   / /______ / /    / __ \ / __ \ / __ \ / _ \ / ___// __/ ");
+    Console.WriteLine(@" / __/  _/ /   / //_____// /___ / /_/ // / / // / / //  __// /__ / /_   ");
+    Console.WriteLine(@"/_/    /___/  /_/        \____/ \____//_/ /_//_/ /_/ \___/ \___/ \__/   ");
+    Console.WriteLine(@"");
+    Console.WriteLine(@"");
+    Console.WriteLine("Demo client for the .NET SDK");
+    Console.WriteLine(@"");
+
+}
+
+public static void PrintSender() {
+    Console.WriteLine(@"        _____                   __");
+    Console.WriteLine(@"       / ___/ ___   ____   ____/ /___   _____");
+    Console.WriteLine(@"       \__ \ / _ \ / __ \ / __  // _ \ / ___/");
+    Console.WriteLine(@"      ___/ //  __// / / // /_/ //  __// /    ");
+    Console.WriteLine(@"     /____/ \___//_/ /_/ \__,_/ \___//_/     ");
+}
+
+public static void PrintSubscriber() {
+    Console.WriteLine(@"        _____         __                       _  __");
+    Console.WriteLine(@"       / ___/ __  __ / /_   _____ _____ _____ (_)/ /_   ___   _____");
+    Console.WriteLine(@"       \__ \ / / / // __ \ / ___// ___// ___// // __ \ / _ \ / ___/");
+    Console.WriteLine(@"      ___/ // /_/ // /_/ /(__  )/ /__ / /   / // /_/ //  __// /    ");
+    Console.WriteLine(@"     /____/ \__,_//_.___//____/ \___//_/   /_//_.___/ \___//_/     ");
+}
+// @formatter:on
+
+    #endregion
+}
diff --git a/DemoRunner/Program.cs b/DemoRunner/Program.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ad63d7bab9cd3511626c31906ff554bb8aa1b65d
--- /dev/null
+++ b/DemoRunner/Program.cs
@@ -0,0 +1,28 @@
+// See https://aka.ms/new-console-template for more information
+
+using DemoRunner;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+OutputHelper.PrintFitConnect();
+
+#region Preparation
+
+// Load appsettings.json and create a configuration object
+var config = new ConfigurationBuilder()
+    .AddJsonFile("appsettings.json")
+    .Build();
+
+// Create a new instance of the logger.
+var logger = LoggerFactory.Create(
+    builder => {
+        builder.AddSimpleConsole();
+        builder.SetMinimumLevel(LogLevel.Trace);
+    }).CreateLogger("FIT-Connect");
+
+#endregion
+
+logger.LogInformation("Starting FIT-Connect");
+
+SenderDemo.Run(config, logger);
+SubscriberDemo.Run(config, logger);
diff --git a/DemoRunner/SenderDemo.cs b/DemoRunner/SenderDemo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e1735afffd8a09137a66448648db979c766ba405
--- /dev/null
+++ b/DemoRunner/SenderDemo.cs
@@ -0,0 +1,26 @@
+using FitConnect;
+using FitConnect.Models;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace DemoRunner;
+
+public static class SenderDemo {
+    public static void Run(IConfigurationRoot config, ILogger logger) {
+        var clientId = config["FitConnect:Sender:ClientId"];
+        var clientSecret = config["FitConnect:Sender:ClientSecret"];
+        var destinationId = config["FitConnect:Sender:DestinationId"];
+        var leikaKey = config["FitConnect:Sender:LeikaKey"];
+
+
+        OutputHelper.PrintSender();
+        var submission = Client
+            .GetSender(FitConnectEnvironment.Testing, clientId, clientSecret, logger)
+            .WithDestination(destinationId)
+            .WithServiceType("FIT Connect Demo", leikaKey)
+            .WithAttachments(new[] { new Attachment("Test.pdf", "Test Attachment") })
+            .WithData("{\"message\":\"Hello World\"}")
+            .Submit();
+        
+    }
+}
diff --git a/DemoRunner/SubscriberDemo.cs b/DemoRunner/SubscriberDemo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..16047cf3e0a8c34eee0900eedecdf8899580ecd6
--- /dev/null
+++ b/DemoRunner/SubscriberDemo.cs
@@ -0,0 +1,43 @@
+using FitConnect;
+using FitConnect.Models;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace DemoRunner;
+
+public static class SubscriberDemo {
+    public static void Run(IConfiguration config, ILogger logger) {
+        var clientId = config["FitConnect:Subscriber:ClientId"];
+        var clientSecret = config["FitConnect:Subscriber:ClientSecret"];
+        var destinationId = config["FitConnect:Subscriber:DestinationId"];
+        var privateKeyDecryption = config["FitConnect:Subscriber:PrivateKeyDecryption"];
+        var privateKeySigning = config["FitConnect:Subscriber:PrivateKeySigning"];
+        var publicKeyEncryption = config["FitConnect:Subscriber:PublicKeyEncryption"];
+        var publicKeySignatureVerification =
+            config["FitConnect:Subscriber:PublicKeySignatureVerification"];
+        var setPublicKey = config["FitConnect:Subscriber:SetPublicKey"];
+
+
+        OutputHelper.PrintSubscriber();
+
+        var subscriber = Client.GetSubscriber(FitConnectEnvironment.Testing, clientId,
+            clientSecret,
+            File.ReadAllText(privateKeyDecryption),
+            File.ReadAllText(privateKeySigning),
+            File.ReadAllText(publicKeyEncryption),
+            File.ReadAllText(publicKeySignatureVerification),
+            logger);
+
+        var submissions = subscriber.GetAvailableSubmissions();
+
+        foreach (var submission in submissions) {
+            var subscriberWithSubmission = subscriber.RequestSubmission(submission.SubmissionId);
+            var attachments = subscriberWithSubmission
+                .GetAttachments();
+
+            logger.LogInformation("Fachdaten: {Data}", subscriberWithSubmission.GetDataJson());
+            subscriberWithSubmission
+                .AcceptSubmission();
+        }
+    }
+}
diff --git a/DemoRunner/Test.pdf b/DemoRunner/Test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..50a038977b732f8c86889b164166410d5d60e0de
Binary files /dev/null and b/DemoRunner/Test.pdf differ
diff --git a/DemoRunner/appsettings.json.template b/DemoRunner/appsettings.json.template
new file mode 100644
index 0000000000000000000000000000000000000000..22d1b2e054b134a606deab61659be4b4a24f0f9c
--- /dev/null
+++ b/DemoRunner/appsettings.json.template
@@ -0,0 +1,21 @@
+{
+  "FitConnect": {
+    "Sender": {
+      "ClientId": "00000000-0000-0000-0000-000000000000",
+      "ClientSecret": "",
+      "DestinationId": "00000000-0000-0000-0000-000000000000",
+      "LeikaKey": "urn:de:fim:leika:leistung:99400048079000"
+    },
+    "Subscriber": {
+      "ClientId": "00000000-0000-0000-0000-000000000000",
+      "ClientSecret": "",
+      "DestinationId": "00000000-0000-0000-0000-000000000000",
+      "LeikaKey": "urn:de:fim:leika:leistung:00000000000000",
+      "PrivateKeyDecryption": "./encryptionKeys/privateKey_decryption.json",
+      "PrivateKeySigning": "./encryptionKeys/privateKey_signing.json",
+      "PublicKeyEncryption": "./encryptionKeys/publicKey_encryption.json",
+      "PublicKeySignatureVerification": "./encryptionKeys/publicKey_signature_verification.json",
+      "SetPublicKey": "./encryptionKeys/set-public-keys.json"
+    }
+  }
+}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..0e4d7c5684d013f5b4dff1b0301cd7a4bf5df961
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+WORKDIR /test
+
+COPY . . 
+RUN dotnet test EncryptionTests/EncryptionTests.csproj
+
+RUN dotnet build DummyClient/DummyClient.csproj -c Release -o /test/build
+
+FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
+COPY --from=build /test/build .
+
+CMD ["dotnet", "DummyClient.dll"]
\ No newline at end of file
diff --git a/Documentation/documentation.de-DE.md b/Documentation/documentation.de-DE.md
new file mode 100644
index 0000000000000000000000000000000000000000..24fb1aab9576d4eff7efe71eb64cb18695d72a76
--- /dev/null
+++ b/Documentation/documentation.de-DE.md
@@ -0,0 +1,93 @@
+# FIT-Connect .NET SDK User Guide
+
+## Einleitung
+
+Das FIT-Connect .NET SDK bietet eine einfache Möglichkeit, sowohl einen Antragsteller (Sender) als auch einen Antragsempfänger (Subscriber) an FIT-Connect anzubinden.
+
+## Voraussetzungen
+
+### OSX
+
+Auf OSX wird das SDK nur dann unterstützt, wenn OpenSSL auf dem System installiert ist.
+
+```sh
+brew install openssl@1.1
+```
+
+Die Environment-Variable ```DYLD_LIBRARY_PATH``` muss auf den Pfad zu OpenSSL verweisen.
+
+_Beispiele:_
+
+```sh
+export DYLD_LIBRARY_PATH=/usr/local/opt/openssl/lib
+export DYLD_LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib
+```
+
+### Sender
+
+Um einen Antrag mit dem SDK versenden zu können, werden eine ClientID und ein ClientSecret benötigt.
+Diese können im FIT-Connect Self-Service-Portal erzeugt werden.
+
+[Offizelle Dokumentation von FIT-Connect zum Versenden von Einreichungen (Anträgen)](https://docs.fitko.de/fit-connect/docs/sending/overview)
+
+### Subscriber
+
+Der Subscriber benötigt sowohl eine ClientID und das ClientSecret, aber auch die Schlüsselpaare zur
+Verschlüsselung wie auch zum Signieren der Daten.
+Zu Testzwecken können selbstgenerierte Schlüsselpaare mit dem [hierzu bereitgestellten Tool](https://docs.fitko.de/fit-connect/docs/details/jwk-creation) erzeugt werden.
+
+In der Produktivumgebung müssen hierzu [Zertifikate der Verwaltungs-PKI zu Einsatz kommen](https://docs.fitko.de/fit-connect/docs/receiving/certificate).
+
+[Offizielle Dokumentation von FIT-Connect zum Abrufen von Einreichungen](https://docs.fitko.de/fit-connect/docs/receiving/overview)
+
+# Beispiele
+
+## Sender
+
+```csharp
+var submission = Client
+    .GetSender(FitConnectEnvironment.Development, clientId, clientSecret, logger)
+    .WithDestination(destinationId)
+    .WithServiceType("FIT Connect Demo", leikaKey)
+    .WithAttachments(new Attachment("Test.pdf", "Test Attachment"))
+    .WithData("{\"message\":\"Hello World\"}")
+    .Submit();
+```
+
+## Subscriber
+
+### Erstellen des Subscribers
+
+```csharp
+var subscriber = Client.GetSubscriber(FitConnectEnvironment.Development, clientId,
+    clientSecret,
+    privateKeyDecryption,
+    privateKeySigning,
+    publicKeyEncryption,
+    publicKeySignatureVerification,
+    logger);
+```
+
+### Abrufen der Submissions
+
+```csharp
+var submissions = subscriber.GetAvailableSubmissions();
+```
+
+### Abrufen der Submissions mit den Anhängen
+
+```csharp
+foreach (var submission in submissions) {
+    var subscriberWithSubmission = subscriber.RequestSubmission(submission.SubmissionId);
+    var data = subscriber.GetDataJson();
+    var attachments = subscriberWithSubmission
+        .GetAttachments();
+    // Submission accept
+    subscriberWithSubmission
+        .AcceptSubmission();
+    // or submission reject
+   subscriberWithSubmission
+        .RejectSubmission();
+
+}
+```
diff --git a/Documentation/documentation.en-EN.md b/Documentation/documentation.en-EN.md
new file mode 100644
index 0000000000000000000000000000000000000000..f5eaea6d26fdaffa64af48e4ce784a2a4e5e2075
--- /dev/null
+++ b/Documentation/documentation.en-EN.md
@@ -0,0 +1,82 @@
+# FIT-Connect .NET SDK User Guide
+
+## Introduction
+
+The SDK supports a high-level support as well as a low-level support for the
+FIT-Connect API and a fluent API.
+
+## All in one
+
+For not having to deal with the low-level API, the SDK provides a high-level support.
+
+### Sender
+
+```mermaid
+flowchart TD
+    client([Create a new FitConnect client])
+    send.submission[Create new Submission]
+    send.send[Send the submission]
+    send.return[\Return the result/]
+    
+    client-->send.submission-->send.send-->send.return
+    
+    
+    subscribe.request[Request submissions]
+    
+    client-->subscribe.request-->ToBeDefined
+```
+
+Simplified call:
+
+```csharp
+bool SendSubmission(Submission submission){
+    var client = new Client(...);
+    return client.SendSubmissionAsync(submission);
+}
+```
+
+### Subscriber
+
+### Example
+
+Visit [All in one](./all_in_one.md) to see the code.
+
+## Fluent Api
+
+### Sender
+
+```mermaid
+stateDiagram
+    [*]-->Authenticate
+    Authenticate-->CreateSubmission
+    CreateSubmission-->UploadAttachments
+    UploadAttachments-->SendSubmission
+    SendSubmission-->[*]
+```
+
+### Subscriber
+
+```mermaid
+stateDiagram
+    [*]-->Authenticate
+     Authenticate-->GetSubmissions
+    GetSubmissions-->[*]
+    
+    Authenticate-->GetSubmission
+    GetSubmission-->GetAttachments
+    GetAttachments-->DecryptAttachments
+    DecryptAttachments-->DecryptData
+    DecryptData-->DecryptMetadata
+    DecryptMetadata-->[*]
+```
+
+#### Receiving list of submissions
+
+#### Receiving specific submission
+
+[Fluent Api Example](./fluent_api.md)
+
+## Detailed calls
+
+[Detailed calls](./detailed_calls.md)
+
diff --git a/DummyClient/Dockerfile b/DummyClient/Dockerfile
deleted file mode 100644
index 2e2e405808c74fafa716b3126ca341d547c3dbfb..0000000000000000000000000000000000000000
--- a/DummyClient/Dockerfile
+++ /dev/null
@@ -1,18 +0,0 @@
-FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
-WORKDIR /app
-
-FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
-WORKDIR /src
-COPY ["DummyClient/DummyClient.csproj", "DummyClient/"]
-RUN dotnet restore "DummyClient/DummyClient.csproj"
-COPY . .
-WORKDIR "/src/DummyClient"
-RUN dotnet build "DummyClient.csproj" -c Release -o /app/build
-
-FROM build AS publish
-RUN dotnet publish "DummyClient.csproj" -c Release -o /app/publish
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "DummyClient.dll"]
diff --git a/DummyClient/DummyClient.csproj b/DummyClient/DummyClient.csproj
deleted file mode 100644
index e449bbc26417523e8a0a2dc0ac0878d7f2f50811..0000000000000000000000000000000000000000
--- a/DummyClient/DummyClient.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-    <PropertyGroup>
-        <OutputType>Exe</OutputType>
-        <TargetFramework>net6.0</TargetFramework>
-        <ImplicitUsings>enable</ImplicitUsings>
-        <Nullable>enable</Nullable>
-        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
-    </PropertyGroup>
-
-    <ItemGroup>
-        <ProjectReference Include="..\FitConnect\FitConnect.csproj"/>
-    </ItemGroup>
-
-</Project>
diff --git a/DummyClient/Program.cs b/DummyClient/Program.cs
deleted file mode 100644
index b5d49cb0fc4ae21f7144eeac2beedb70b2a60202..0000000000000000000000000000000000000000
--- a/DummyClient/Program.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using FitConnect;
-using FitConnect.Models;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-
-Client client;
-
-
-void FluentSenderCall() {
-    client.Sender
-        .WithDestination("destinationId")
-        .WithAttachments(Array.Empty<Attachment>())
-        .WithData(@"{""data"":""content""}")
-        .Submit();
-
-    client.Sender
-        .WithDestination("destinationId")
-        .WithAttachments(Array.Empty<Attachment>())
-        .Submit();
-}
-
-void FluentSubscriberCall() {
-    var submissions = client.Subscriber
-        .GetAvailableSubmissions("destinationId");
-
-    client.Subscriber.RequestSubmission("submissionId")
-        .GetAttachments((attachments => {
-            // Check if the attachments are valid
-            return true;
-        }));
-}
-
-
-ILogger logger = new Logger<AppDomain>(new NullLoggerFactory());
-client = new Client(
-    FitConnectEnvironments.Create(FitConnectEnvironments.EndpointType.Development),
-    "clientId", "clientSecret",
-    logger);
-
-Console.WriteLine(
-    "This is a dummy client to demonstrate the usage of the FitConnect SDK for .NET");
-
-FluentSenderCall();
-FluentSubscriberCall();
diff --git a/E2ETest/E2ETest.csproj b/E2ETest/E2ETest.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..9b68a3a077dc80a2ef5cfae445e642b7cf745aa3
--- /dev/null
+++ b/E2ETest/E2ETest.csproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
+        <PackageReference Include="NUnit" Version="3.13.3" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
+        <PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
+        <PackageReference Include="coverlet.collector" Version="3.1.2" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\FitConnect\FitConnect.csproj" />
+      <ProjectReference Include="..\MockContainer\MockContainer.csproj" />
+    </ItemGroup>
+
+</Project>
diff --git a/E2ETest/EndToEndTestBase.cs b/E2ETest/EndToEndTestBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d6a15f6d892d30fc7d46edd13943ded703cf7cc9
--- /dev/null
+++ b/E2ETest/EndToEndTestBase.cs
@@ -0,0 +1,39 @@
+using Autofac;
+using FitConnect;
+using FitConnect.Interfaces.Sender;
+using FitConnect.Interfaces.Subscriber;
+using FitConnect.Models;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+
+namespace E2ETest;
+
+public abstract class EndToEndTestBase {
+    private ISender _sender;
+    private ISubscriber _subscriber;
+
+    [OneTimeSetUp]
+    public void Setup() {
+        var container = MockContainer.Container.Create();
+        var settings = container.Resolve<MockSettings>();
+
+        var logger = LoggerFactory.Create(
+            builder => {
+                builder.AddSimpleConsole();
+                builder.SetMinimumLevel(LogLevel.Trace);
+            }).CreateLogger("E2E Test");
+
+
+        _sender = Client.GetSender(FitConnectEnvironment.Testing,
+            settings.SenderClientId, settings.SenderClientSecret,
+            logger);
+        _subscriber = Client.GetSubscriber(FitConnectEnvironment.Testing,
+            settings.SenderClientId, settings.SenderClientSecret,
+            settings.PrivateKeyDecryption,
+            settings.PrivateKeySigning,
+            settings.PublicKeyEncryption,
+            settings.PublicKeySignatureVerification,
+            logger
+        );
+    }
+}
diff --git a/E2ETest/RejectSubmissionTest.cs b/E2ETest/RejectSubmissionTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7279f8a56631301dba4a6028163c5218fac6fe87
--- /dev/null
+++ b/E2ETest/RejectSubmissionTest.cs
@@ -0,0 +1,6 @@
+using FitConnect.Encryption;
+
+namespace E2ETest;
+
+public class RejectSubmissionTest : EndToEndTestBase {
+}
diff --git a/E2ETest/StraightForwardTest.cs b/E2ETest/StraightForwardTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f7b1e3312263ed59927209fb201b57564af5b97d
--- /dev/null
+++ b/E2ETest/StraightForwardTest.cs
@@ -0,0 +1,9 @@
+namespace E2ETest;
+
+public class StraightForwardTest : EndToEndTestBase {
+    [Order(10)]
+    [Test]
+    public void Test1() {
+        Assert.Pass();
+    }
+}
diff --git a/E2ETest/Usings.cs b/E2ETest/Usings.cs
new file mode 100644
index 0000000000000000000000000000000000000000..324456763afb873e116da92178ad9018cb9e7e50
--- /dev/null
+++ b/E2ETest/Usings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
diff --git a/EncryptionTests/Certificates/certificate.cer b/EncryptionTests/Certificates/certificate.cer
new file mode 100644
index 0000000000000000000000000000000000000000..73f0387ee1b1ff8bdc9fe430d24b328c0e860b85
--- /dev/null
+++ b/EncryptionTests/Certificates/certificate.cer
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEyjCCArKgAwIBAgIIMIw7HOs7698wDQYJKoZIhvcNAQELBQAwJTEWMBQGA1UEAxMNZml0Y29u
+bmVjdC5kZTELMAkGA1UEBhMCREUwHhcNMjIwNjA4MTEwNzQ3WhcNMjcwNjA4MTEwNzUyWjAlMRYw
+FAYDVQQDEw1maXRjb25uZWN0LmRlMQswCQYDVQQGEwJERTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAJxxbIieIEY9Vvql4R6ED5HqtGMjoJZ2xiwF5Zv/DuLseAZEUR7QuBvPG8CLcYOE
+JArXcAS5smLfG2LiNerMgDnrLGvBl3qrYOEq0kRIqGkSr5JM2V/bk2UgMToOJGQDBMh6IbU707Xp
+u1gHe2wuNxbJE4kALZV8zJX9fE2bc87o3URsw4yv3EnCpR3TskPI2uazNxKWVWjgpZ5yB6PQMbG0
+R7e0qGdEeIo5PpaxdkzbJUN4LwnEdZCKbXt3bMqr7wUJ4lXrqCUhO1Y57iR5YoNBTkKgJHV9fbcy
+EHs6IZAhxQCCyuczby5UDS4VzXvnqpxxnvI/rv12nc2WPPctvmA3AyHOH7hTugg7UZ75UKFFHqfU
+TeY6lWkifUpTpWVZ3Krm0+3Ja/VZzufPSybifTR9v5JMxQdwaqp9E53Cqh1csqciLVazDOjU6Lhn
+fjLr4GwSri1FveXDqpq/jJhkTbuDL1fAEs+eDauD3vNxb0Bverpb5/7rOcAKTH0GEzZhSHV23fyP
+DpKmpi5h0py42SdbBAZat8C1StMQ7m3UxuDdoMg+zRS5d12d6s0ZFf2xwFCjDVNVN6y7BkSGuA2l
+2/I5ojXhK/Wm+DAcKT/osZznyNq4OlHL2ck6LOSSgFCJFrdkpku++gROx2xjUIFQjryUkcPZ6y0l
+PLXAHJUEGO97AgMBAAEwDQYJKoZIhvcNAQELBQADggIBADXJ6LP4HdnUiFCgQR8PoY+fR8XCnYx8
+FdVB9v9xdsBhE1/B1c8z6kUInQdq2NfsRjyfgA1wIdPElP+b1bu/TKS/GpWrsPHUXDRv67qT13cB
+rw8I6rHTDdnF00pEU4mabI32EqGnEAwu8DGlKnDbnh/dY7IQjYK40/ZutcqzybzvBiNFoYu7rKXg
+7CThPwc92fnPfFcDJJMU9YlA5C6MaLSxDj0e3z1rl+ew9pab8gclnbzgxGOqFMNPhNRHx0SvFrCF
+dPzva9mYDATHgR66hJAo/hov8qJnz6/7xkQgFPy4jHwBk2ubM2SN8+UEyklm7u3v8X6mpvdE7/gC
+qWJYJjcpzz7QEBmvJ9yYIALNo8E9UyZNfYgE/hEyBpEccNk7z6y/yHNThcojwbfmYjztl5Ed4uHY
+i1ycf5LYcSAxtzD6V5CuIVzmPNkSgB1m8Wu/+5Sy2/uQGIKNK/X6E9f1GjTDBv5KzxOoongc4yri
+VinYgmrpoF6Uh6G423IGT6/+SpyQa5oegpSbKKDcSAQKK/fCJQbck03WTbpgKDhc0KDLB2C07yiI
+RNgSDRO+eg8BTFjxE3uPVwY8AP+QxK2+Xn5ozuT7aedeS41MQYeeatd6StNy809DvIMb44ZUA6JU
+/n8Ok8eHWVY+aShfomcLyH7+EeL9e2LTSp0geUdmsnNI
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/EncryptionTests/Certificates/certificate.pfx b/EncryptionTests/Certificates/certificate.pfx
new file mode 100644
index 0000000000000000000000000000000000000000..ec4092f2c68e0c8865fe9333be770dd09fb5c6f9
Binary files /dev/null and b/EncryptionTests/Certificates/certificate.pfx differ
diff --git a/EncryptionTests/EncryptionTests.csproj b/EncryptionTests/EncryptionTests.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..86c48ff27f44f56c75667c55b9446ac99c37e00a
--- /dev/null
+++ b/EncryptionTests/EncryptionTests.csproj
@@ -0,0 +1,40 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+
+        <RootNamespace>SenderTest</RootNamespace>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="FluentAssertions" Version="6.7.0"/>
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/>
+        <PackageReference Include="NUnit" Version="3.13.2"/>
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/>
+        <PackageReference Include="coverlet.collector" Version="3.1.0"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\FitConnect\FitConnect.csproj"/>
+        <ProjectReference Include="..\MockContainer\MockContainer.csproj"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Folder Include="Certificates"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <None Update="assets\attachment.pdf">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </None>
+        <None Update="Test.pdf">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+
+</Project>
diff --git a/EncryptionTests/FileEncryptionTest.cs b/EncryptionTests/FileEncryptionTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..457630b02e541c551ac5f81a6a34c3133a7543ca
--- /dev/null
+++ b/EncryptionTests/FileEncryptionTest.cs
@@ -0,0 +1,46 @@
+using System.IO;
+using System.Security.Cryptography;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Models;
+using FluentAssertions;
+using MockContainer;
+using NUnit.Framework;
+
+namespace SenderTest;
+
+public class FileEncryptionTest {
+    private string _encryptedFile;
+    private FitEncryption _encryption;
+    private byte[] sourceFile = null!;
+
+    [SetUp]
+    public void Setup() {
+        sourceFile = RandomNumberGenerator.GetBytes(4096);
+        var container = Container.Create();
+        var keySet = container.Resolve<KeySet>();
+        _encryption = new FitEncryption(keySet, null);
+    }
+
+    [Test]
+    [Order(10)]
+    public void EncryptFile() {
+        _encryptedFile = _encryption.Encrypt(sourceFile);
+    }
+
+    [Test]
+    public void TestSha512FileHash() {
+        // Arrange
+        var content = File.ReadAllBytes("Test.pdf");
+
+
+        // Act
+        var attachment = new Attachment("Test.pdf", "Just an attachment");
+
+        var hash = attachment.Hash;
+        // Assert
+        hash.Should()
+            .Be(
+                "8b1042900c2039f65fe6c4cb1bca31e2a7a04b61d3ca7d9ae9fc4077068b82ad5512fa298385b025db70551113b762064444b87737e45e657a71be5b88b06e59");
+    }
+}
diff --git a/EncryptionTests/JweTest.cs b/EncryptionTests/JweTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..207a5689f55a99833542c8673b16220fc5b18dcc
--- /dev/null
+++ b/EncryptionTests/JweTest.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Security.Cryptography;
+using Autofac;
+using FitConnect;
+using FitConnect.Encryption;
+using FitConnect.Models;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace SenderTest;
+
+public class JweTest {
+    private IContainer _container;
+    private ILogger<JweTest> _logger;
+    private Sender _sender;
+    private MockSettings _settings;
+
+
+    [SetUp]
+    public void SetUp() {
+        _logger = LoggerFactory.Create(cfg => cfg.AddConsole())
+            .CreateLogger<JweTest>();
+
+        _sender = new Sender(
+            FitConnectEnvironment.Testing,
+            "", "",
+            _logger);
+
+        _container = Container.Create();
+        _settings = _container.Resolve<MockSettings>();
+    }
+
+
+    [Test]
+    public void TestJwe() {
+        // Arrange
+        var engine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PublicKeyEncryption = _settings.PublicKeyEncryption,
+            PrivateKeyDecryption = _settings.PrivateKeyDecryption
+        };
+
+        // Act
+        var dummyText = JsonConvert.SerializeObject(new { Title = "Value", Content = "content" });
+        Console.WriteLine(dummyText);
+        var cypher = engine.Encrypt(dummyText);
+        Console.WriteLine(cypher);
+        var plain = engine.Decrypt(cypher);
+        Console.WriteLine(plain.plainText);
+
+        // Assert
+        plain.plainText.Should().Be(dummyText);
+    }
+
+    [Test]
+    public void TestJwe_withBytes() {
+        // Arrange
+        var engine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PublicKeyEncryption = _settings.PublicKeyEncryption,
+            PrivateKeyDecryption = _settings.PrivateKeyDecryption
+        };
+
+        // Act
+        var plainBytes = RandomNumberGenerator.GetBytes(8192);
+        var cypher = engine.Encrypt(plainBytes);
+        var plain = engine.Decrypt(cypher);
+
+
+        // Assert
+        plain.plainBytes.Should().BeEquivalentTo(plainBytes);
+    }
+
+    [Test]
+    public void TestMetaData() {
+        // Arrange
+        var encryptionEngine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PublicKeyEncryption = _settings.PublicKeyEncryption
+        };
+
+        var decryptEngine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PrivateKeyDecryption = _settings.PrivateKeyDecryption
+        };
+
+
+        var metaData =
+            "{\"attachments\":[{\"attachmentId\":\"5d055f43-4ad6-4202-822a-f946c3be29a6\",\"description\":\"Just a test\",\"filename\":\"RandomBytes\",\"hash\":{\"content\":\"8b1042900c2039f65fe6c4cb1bca31e2a7a04b61d3ca7d9ae9fc4077068b82ad5512fa298385b025db70551113b762064444b87737e45e657a71be5b88b06e59\",\"type\":0},\"mimeType\":\"application/pdf\",\"purpose\":0}],\"data\":{\"hash\":null,\"submissionSchema\":{\"mimeType\":0,\"schemaUri\":\"urn:de:fim:leika:leistung:99400048079000\"}}}";
+
+        // Act
+        var encrypted = encryptionEngine.Encrypt(metaData);
+        var plain = decryptEngine.Decrypt(encrypted);
+
+        // Assert
+        plain.plainText.Should().Be(metaData);
+    }
+
+    [Test]
+    public void TestJwe_withBytes2() {
+        // Arrange
+        var engine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PublicKeyEncryption = _settings.PublicKeyEncryption,
+            PrivateKeyDecryption = _settings.PrivateKeyDecryption
+        };
+
+        // Act
+        var plainBytes = RandomNumberGenerator.GetBytes(512);
+        var cypher = engine.Encrypt(Convert.ToBase64String(plainBytes));
+        var plain = engine.Decrypt(cypher);
+
+        // Assert
+        plain.plainText.Should().BeEquivalentTo(Convert.ToBase64String(plainBytes));
+    }
+
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.R5TmfiDyi7dZhYz9NxduhqZc6GrfAERrDVGNLts6eHhU2SVMTAja1O9Rywrf_thPvbjQslM6ukXB8iI3hI6oWN2EY9SqUjKRto3zL2jdmdcXzfwxJFm1BnObMjYl9Jwy2ogEwLldXQhLAwMxVJWJbf31mZCgnVYC6DbPTEi-GLIkLDEn7Tj5y9iak3TjaE0hIxMk546Dda-q6I9QQ7YlDGV8m0Ijjh21yu2_B9H3Uh9LgIyrTN1Jw2saJRKVJgrtoV6e8WRaj5sDXarLNz6R2LhEtIjYjVioZvjBt1EeXADjcR9m1j3qG8V1f9boVocRTivvvaRvWd2NTH5yFGunDXr40oUmXXWXK1SfKsty85AAjlLOv5ZJsU1vhquA2XDgDVDJ7Nm1qC_9VeW2yCD01Ewh9sGiKscMqP1CbLxyVPfbuViVIe4g0h7Krlb6mZe9L7Sk18cLyFuCre7nYfB5ZToKnSkd8C5-ExMHfrcp9MJ196X6_n7YFKrc-Lzg7tvCRBG-6DFIQ1iqnme6crMN15qwo9VddOJOFXV815vNVyFXLhJAvr85q78aqyaE90qgY2QDNXyZXziTbXLurISL8i_Mdzt0J7cUyrvQLZaK0_pMJJvZOg3LvNalTGdJjUHUFwPxTbe-DRWZuPT81KVNyNa8EKkqox1Ohm_56riMFyU.Od1O-XCSw2Xa8wBN.mHWq8eodcIDXKWjMDDqcNkunVLT0EvjvAWq28gc6aOfKN0zeT0SQczxl0jqcWlziHXE8KmDL7CDYhOZT4rv--lN6wDNj998.YjJfxro1W_ERT7jOvI06yA")]
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.NWLSv3Yq3IK2oNVgbDoVpcJ-i3iunOOWQhoULQRa5Haf4g2E9dJOgq7jPqUl347jRB6pkyj09PL9b2R5PUai5Z6KraW6vCOQM4zeA_Z3SAjONO2XNNDQZpA2TvsxrEXqG_89ck8rMOHnkFJBUrQp4jnwFeIswQQTtErdbsTXTyYdkuJk9j18-yrZM0px9FkbB3u5Fqn8Nu5GGRAVGBw_u73QbNbfgmZINgn-2WHCAdqAW5U776XnGSYxf4qIUcwzA8JjFOUU-0l3KqgvMTsN-f0XcHEGDqvxOWCVsgvibfMZ8rnAowirX6z8tIWrVCvqE-5jOX9gXe-xXMYTAi-a0xU2f1ZrJGcfoMoUL8-kePytbU4JdudNgx_F60WGqVI3wnLAj7V_u0mFumsaR6QjrasInNS7Dqi_Rg3T9wZFpa4_dUOx1j6z_r5Z7_GVV4fypli_prWJeN_34iazEfshYw33QngNMKYjNSvGb9JxYabZyTrOJImJsK503Ad_3sTAi4AENsKAnesz1GUCxkz-hONKEX9QfG3rxqt0PPXmX6Dm5NdbyWdUBSWu47eSv0sSH2xoj0Oov0c_c8JzdvkV82eNQRwyqezjSCq6NwmHtIJL_j4b79tqHWhWauXo24xsYLIA8qNpLIH9UfXEJwG33KQlmiCK-k8IHFWB0-_BzVk.m5zCt2SccAkr4hXF.ROdp1qpWiU_wYDyEjtgZp9XFcqL-Jb7r7u7LPhdWJ7nSSIRJM_UYxBUFiF3XOFL22r_67ml8slap7ylN5TGRQxtbtm-Yq_T2y9Df33SozoFY-2pIZg9mzUxN0hbGil5REbmleYyRfY7NsMDkZjWDXlOoQeI7ZjmnXBEEdtOam2-ccOW94rJE-xy7HdwZMdPgakSz9-KPtDPJQMRyMi1C9wUZ_R1FND4sSmjlUmLgJ-dywdIRK35bQMu5WEjlo9JoWxu8Aw8C-vjF5zXXNSMsn3W2YC1T3O9eGBNcVdA1-R_IgkC8lJqL_uwKMQuk5tB2bSO3wdjweVxszouvy3TzHWot75f6rTWbw1NphtSN3G3YUDnBIbnFG0-kU7gLCf3fvmIsL8mf0vopAN83Cwx9hJA.IU6vJRAhYR30TizHP07neQ")]
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.srb2oytHYxTqcaLZiGNQX4QJqBOiEPZAgO4sZJdBA-koWu6-wliPy7YyqYc8-bCMAbiwp9XOoR8wRKK7pnaIsU66k9zCp1FjVcjkE420URdwagzPqqKoxcyr7wNIcAWjA8iMfXB1KlxVHbt4nryspoGwSvLMj7CxBkzOcAUw9naNV0B-RjNrtdpt7scYyBVlCjl9Rj4Qy1W1BGviU5VHYkLJa7NXQ34amTyXBRoTtxhAP3HIu8DXTnbWhyToU25THWswSOy7rNaLOqfsXLp0ecfpoVKXXjDjMjhcCo7o6QJO1Jc6IBKdj7OGhIHCJOQAZ0jE9XchZXR9oxIDlG4qV-JKKhldRtfkJdmIZ4Vx-jhMweaqu2LQy0ZA3F3JrtvjKhTiJFQrg-JVaUQsY_zTKmTKnZis1KsjkKNwdKi_oc5Ug1DAH0_WE4IU_pe5bfRdYYMWl6m1wAOoXd3TiKp1vfpDVyeWfeg-gohiuK0lszoVfiiA97Y5wtk5NpNpzKNThQecHoyRhAj3_E5iRpztjnqHh0I0BSQJFMVqdBjsQU4zZjRhkxsU1jVH_004jVLNyAgJ9jpB1UQzahMsTJsHPuhqvRu8-tjwK1juuDpZXctUdbSzYO8pDTbHz6ysDazyEniGqQFKWj1AEg2chGm2uVtoeFkkAPEZTkdv2Gsr2sA.4re8HD8VxtnSEjc8.hXDdDN4X1M1M9eB-2Kf4WemzOXlSKFeEAR8L_3KBOgAprGDIZygOxSAyBH0sSISMHk7SvSYwtt3HXbmB1hM12ZbQRLbFknLOv6Pw8Lr8CvVheOl70SKMkocx0HHlYXYp3r4ZrTY3AKlHpX3ePoHoMWXStgOt5EWKG5ciW018sFHKVKQG87hMgSvDt4FoZAN4yyHsMn9Sf5DB3LhNyAXyx-4Oz8q9q-M-X-ckYEcnJfZl_G4SOQPBa-Ly7MDEeNQ9fRHozbdHmZ1TzLFF4xZbda12V15fL_JNWUzwuhUL589l-RR119FQlKTxv6Unes3o0gdSz7-l0hQI3Q2eKdf0Jj_CMw8f-yPO89YSgVxbK452YxHK8JWKMCcweNNHhMBjFUssFcBpuBXcHeekjVbcYA.ADUZnHvl9fM3OmLeaibVuA")]
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.R5TmfiDyi7dZhYz9NxduhqZc6GrfAERrDVGNLts6eHhU2SVMTAja1O9Rywrf_thPvbjQslM6ukXB8iI3hI6oWN2EY9SqUjKRto3zL2jdmdcXzfwxJFm1BnObMjYl9Jwy2ogEwLldXQhLAwMxVJWJbf31mZCgnVYC6DbPTEi-GLIkLDEn7Tj5y9iak3TjaE0hIxMk546Dda-q6I9QQ7YlDGV8m0Ijjh21yu2_B9H3Uh9LgIyrTN1Jw2saJRKVJgrtoV6e8WRaj5sDXarLNz6R2LhEtIjYjVioZvjBt1EeXADjcR9m1j3qG8V1f9boVocRTivvvaRvWd2NTH5yFGunDXr40oUmXXWXK1SfKsty85AAjlLOv5ZJsU1vhquA2XDgDVDJ7Nm1qC_9VeW2yCD01Ewh9sGiKscMqP1CbLxyVPfbuViVIe4g0h7Krlb6mZe9L7Sk18cLyFuCre7nYfB5ZToKnSkd8C5-ExMHfrcp9MJ196X6_n7YFKrc-Lzg7tvCRBG-6DFIQ1iqnme6crMN15qwo9VddOJOFXV815vNVyFXLhJAvr85q78aqyaE90qgY2QDNXyZXziTbXLurISL8i_Mdzt0J7cUyrvQLZaK0_pMJJvZOg3LvNalTGdJjUHUFwPxTbe-DRWZuPT81KVNyNa8EKkqox1Ohm_56riMFyU.Od1O-XCSw2Xa8wBN.mHWq8eodcIDXKWjMDDqcNkunVLT0EvjvAWq28gc6aOfKN0zeT0SQczxl0jqcWlziHXE8KmDL7CDYhOZT4rv--lN6wDNj998.YjJfxro1W_ERT7jOvI06yA")]
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.dNozKGHneskpBgbTZs8-aA3jCgH4jjMJHPmL2J2EPyysIpt0o2DQa1S7Dn5we88OHz8d3OuC5S2bO0rpI6mKTnJB8RAkFIn9P0LlkvIma915Bp3stoDWW8-VL9LDKV9j0-5K8y7EakTBvu0RxyvCw_WxIkum1w3vKcCBfwgnMU6kyB7h2t_ZgMdBud-Rap2xnN5Xu0TNq0LDKtCvJ2pk0AzExyVoumOg8LocIeonMkQcIRf0l_6aVGzSJXpLPdld2I0NWUGGa8OwTeoqBC9u_28fNBQfbNHdabZcYTEZFpqA4zqMpkAHi-tzzC-BUigRBfKSl5qcYeJQROpzxhkBEY9V7oFPFeY7gh5hKZPVxljtlPbD9nJjTQ_YVJEKd164RlCNNexFtFObxKOCwE4nkY9ouHTseeAVSAwA67LaD-Ap668RxwP6wp6U-KYCyVfXL08HUre9dKYJZwD9jl48F1l5ZPhgrMchBpPjG2o77jISoMGE6PhR2sTbgbfxACh6NrIFujIUz4CuGah8a84mFVznJIws-d_vvRJUaeHPwhkR2WcC7waUkS_Uf7KLlGdAWbZgtO7TjYxaTyTnIxswOBeN1JMos3PYpmkbgOF2LLJ4yx6Aq_pOe8SszMK26sCvQzpfXn28b9JYKMAAplKuQITWtXexW_L-VHeKepMvTTI.BscV4pPkGDfSFNXn.GGwY5fFBj7uK_-MY_ovQqVSkkMRZ7wfEeJNktHeN-hB8UYAN-yykvXpOHO3kAP3C377PF4p7BaoEsbUtdDRLRbS0qBm8qx-2dU6SeWU2kBZ-4xcYaF0hZaH6Lv6QHKuA9cJPtJIGnN9ooqlegFTyjAEvJu_H0SH8n3wnDEe-6sMEFwCwuyiL33G3dneH8-ozTzrD86ZC101BIguBaS5C-NHAyt4GreR8UEgV3Mhqh51qxEO9FsRs9TNzkkaYZTuD9I-UTtIAf01XJ2O_hRawmwMEANjgVun_IzZYSNiDk9uuyi3gznwuqXwu4AjsDuhfhc2c_NR6bAoovaBKCQIyyzn0SwHfnXm8iV-uyc1SeE3IVEOVV5fpsUwI_YLGprbRlF3JCp1RKfcNV67oRfc1vg.UlFSOsFR_h1YGobdj3Umeg")]
+    [TestCase(
+        "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidnI3Y1dLR2wtZzRXYV9DUkdvd05oQVlXX2dRYi1ha01iaWlneE4wRWtESSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ6aXAiOiJERUYifQ.sPKcV2qS_MWepdSwq5_tmg5DtnARKeGOu058CSz6aB04g3AgCUOMNUetotxdRlvW0im9qnwOHUe_75Aty1DSnzv2ZUcsVwjf44g5KFSFb30w1kWccoCW8F2-9uAxHnpOPsM2qT_enUXDLWvt6L2JiX30hTjh62PwEXFzo36FmExi1oqwFM_aOPahHamfSQ_qwZ1cJRPgN67amgM_UAu4fTHccobJPEiKyqB6cejRfvmduLwiu7RmvPWiZA-ROLVu-rUvLWD65OpxOXOjVmLbxeig85qdYUBI8u55PnjBPouP-hqWf55ZFFUSWZgJ_7lYzTpkzAYPw967-9t7jZmXirj7RoSbGs5dvorDpVVQjQS3xg6o7PG63OXy3CLSSVmyVPCwULMaloebvUZKCrzRTjSXSR4uaLfBZFl2oEp2dBJn9ly_g3l6bapI494YH5N6m3MgvSy2zloQqIShcXi2aMoE9eKVm5fuO0UgqdLecJQ_M_4kUBcSITj_pwNiySYJkHXwhJUDY0GzjPe9aNKLGYzD6IpXG27OmFk-C2VtnyaiCDwB_D77KvvgLGdZKBwN8aWRuJxv0S6UtimrVF0rxLI8EtAEyPgaxW0vfSKoCFGYwmLh5Yuwg1AE2PFT-EbAWM3XJB-JlpbdoX5Xj7ZY8CFQfFzeLUxAZjtISzpaxyQ.MkNrLEOLpw3-aYGX.F1wksBj0QSHdbzK9vWUGwroo93PgU2GZxA6TiDTWoNXIqYM4IxtOpBJpQM9-aKKHx99_nyYhatgAYKx8lZ7kLrFE7J-KYnI.rlpSHJrYBeiz-tqULrLPRg")]
+    public void DecryptDataFromServer_None_ShouldWork(string data) {
+        var engine = new FitEncryption(_container.Resolve<KeySet>(), _logger) {
+            PublicKeyEncryption = _settings.PublicKeyEncryption,
+            PrivateKeyDecryption = _settings.PrivateKeyDecryption
+        };
+
+        var plain = engine.Decrypt(data);
+        Console.WriteLine(plain.plainText);
+    }
+}
diff --git a/EncryptionTests/Test.pdf b/EncryptionTests/Test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..50a038977b732f8c86889b164166410d5d60e0de
Binary files /dev/null and b/EncryptionTests/Test.pdf differ
diff --git a/FitConnect.sln b/FitConnect.sln
index d5a5bcffde12205d47fcacd2dc4bfc6b02fc6927..6ebc17706ecb9c24786748c73c19456dce4042d0 100644
--- a/FitConnect.sln
+++ b/FitConnect.sln
@@ -2,7 +2,21 @@
 Microsoft Visual Studio Solution File, Format Version 12.00
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FitConnect", "FitConnect\FitConnect.csproj", "{DFF6A0D9-5AA1-4640-B26C-4A0A28E42FA1}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DummyClient", "DummyClient\DummyClient.csproj", "{DEF51494-6BCD-4441-8D76-6769BBA2C089}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EncryptionTests", "EncryptionTests\EncryptionTests.csproj", "{68F1EC39-B234-4422-9AA8-E55CFA15025D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicUnitTest", "BasicUnitTest\BasicUnitTest.csproj", "{39D5C71C-0723-49B5-8637-BF99346F0060}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MockContainer", "MockContainer\MockContainer.csproj", "{EEF0435D-2865-40C9-8987-3856AA8EBF04}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{180029B5-8DD3-4594-B34E-6C07AF1C52C5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auxiliary", "Auxiliary", "{2F4278C5-FFC9-4080-A1C6-0CE9B5ED2B51}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoRunner", "DemoRunner\DemoRunner.csproj", "{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E2ETest", "E2ETest\E2ETest.csproj", "{8428631D-E396-4685-BE34-5E6C3307EE97}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -14,11 +28,41 @@ Global
 		{DFF6A0D9-5AA1-4640-B26C-4A0A28E42FA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DFF6A0D9-5AA1-4640-B26C-4A0A28E42FA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{DFF6A0D9-5AA1-4640-B26C-4A0A28E42FA1}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DEF51494-6BCD-4441-8D76-6769BBA2C089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DEF51494-6BCD-4441-8D76-6769BBA2C089}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DEF51494-6BCD-4441-8D76-6769BBA2C089}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DEF51494-6BCD-4441-8D76-6769BBA2C089}.Release|Any CPU.Build.0 = Release|Any CPU
+		{73CE5625-4C13-458E-B524-0DAA850F4041}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{73CE5625-4C13-458E-B524-0DAA850F4041}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{73CE5625-4C13-458E-B524-0DAA850F4041}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{73CE5625-4C13-458E-B524-0DAA850F4041}.Release|Any CPU.Build.0 = Release|Any CPU
+		{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{68F1EC39-B234-4422-9AA8-E55CFA15025D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{68F1EC39-B234-4422-9AA8-E55CFA15025D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{68F1EC39-B234-4422-9AA8-E55CFA15025D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{68F1EC39-B234-4422-9AA8-E55CFA15025D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{39D5C71C-0723-49B5-8637-BF99346F0060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{39D5C71C-0723-49B5-8637-BF99346F0060}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{39D5C71C-0723-49B5-8637-BF99346F0060}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{39D5C71C-0723-49B5-8637-BF99346F0060}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EEF0435D-2865-40C9-8987-3856AA8EBF04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EEF0435D-2865-40C9-8987-3856AA8EBF04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EEF0435D-2865-40C9-8987-3856AA8EBF04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EEF0435D-2865-40C9-8987-3856AA8EBF04}.Release|Any CPU.Build.0 = Release|Any CPU
+		{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8428631D-E396-4685-BE34-5E6C3307EE97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8428631D-E396-4685-BE34-5E6C3307EE97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8428631D-E396-4685-BE34-5E6C3307EE97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8428631D-E396-4685-BE34-5E6C3307EE97}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
+		{27115A99-2AE8-42BC-9495-BE2DCEDDF1E8} = {180029B5-8DD3-4594-B34E-6C07AF1C52C5}
+		{68F1EC39-B234-4422-9AA8-E55CFA15025D} = {180029B5-8DD3-4594-B34E-6C07AF1C52C5}
+		{39D5C71C-0723-49B5-8637-BF99346F0060} = {180029B5-8DD3-4594-B34E-6C07AF1C52C5}
+		{EEF0435D-2865-40C9-8987-3856AA8EBF04} = {180029B5-8DD3-4594-B34E-6C07AF1C52C5}
+		{930259A9-A5A9-4BB6-8DA7-4914FB57E7B3} = {2F4278C5-FFC9-4080-A1C6-0CE9B5ED2B51}
+		{8428631D-E396-4685-BE34-5E6C3307EE97} = {180029B5-8DD3-4594-B34E-6C07AF1C52C5}
 	EndGlobalSection
 EndGlobal
diff --git a/DummyClient/DummyClient.csproj.DotSettings b/FitConnect.sln.DotSettings
similarity index 67%
rename from DummyClient/DummyClient.csproj.DotSettings
rename to FitConnect.sln.DotSettings
index 453288bf2b5f62addc7e0fb4b6705154705069ae..f281d4db2dd13bfe3086a7132f4fed01ba09ac88 100644
--- a/DummyClient/DummyClient.csproj.DotSettings
+++ b/FitConnect.sln.DotSettings
@@ -1,2 +1,2 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
-	<s:String x:Key="/Default/CodeInspection/Highlighting/UsageCheckingInspectionLevel/@EntryValue">Off</s:String></wpf:ResourceDictionary>
\ No newline at end of file
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=leika/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/FitConnect/BaseClasses/FunctionalBaseClass.cs b/FitConnect/BaseClasses/FunctionalBaseClass.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f6d5abbe54e1a32b74b6cd735f89b713b74a5138
--- /dev/null
+++ b/FitConnect/BaseClasses/FunctionalBaseClass.cs
@@ -0,0 +1,46 @@
+using FitConnect.Models;
+using FitConnect.Services.Interfaces;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.BaseClasses;
+
+public abstract class FunctionalBaseClass {
+    protected readonly ILogger? Logger;
+    // protected readonly FitConnectApiService ApiService;
+
+    protected IOAuthService OAuthService;
+    protected IRouteService RouteService;
+    protected ISubmissionService SubmissionService;
+
+
+    /// <summary>
+    ///     Constructor for the FunctionalBaseClass
+    /// </summary>
+    /// <param name="environments">FitConnect endpoints</param>
+    /// <param name="logger">ILogger implementation</param>
+    /// <param name="oAuthService"></param>
+    /// <param name="submissionService"></param>
+    /// <param name="routeService"></param>
+    /// <param name="destinationService"></param>
+    /// <param name="certificate">The Encryption certificate</param>
+    /// <example>
+    ///     new Sender(logger, FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development))
+    /// </example>
+    protected FunctionalBaseClass(FitConnectEnvironment? environment,
+        ILogger? logger,
+        IOAuthService oAuthService,
+        ISubmissionService submissionService,
+        IRouteService routeService, IDestinationService destinationService) {
+        DestinationService = destinationService;
+        Environment = environment ??
+                      FitConnectEnvironment.Testing;
+
+        Logger = logger;
+        OAuthService = oAuthService;
+        SubmissionService = submissionService;
+        RouteService = routeService;
+    }
+
+    protected IDestinationService DestinationService { get; }
+    public FitConnectEnvironment Environment { get; }
+}
diff --git a/FitConnect/Client.cs b/FitConnect/Client.cs
index 3146b465a5595f130f35e76764bb7a5669d2467e..5de16d8f385d049e05fef785750f118498a5d934 100644
--- a/FitConnect/Client.cs
+++ b/FitConnect/Client.cs
@@ -1,36 +1,55 @@
-using System;
-using System.Threading.Tasks;
+using Autofac;
+using FitConnect.Interfaces.Sender;
+using FitConnect.Interfaces.Subscriber;
+using FitConnect.Models;
 using Microsoft.Extensions.Logging;
 
 namespace FitConnect;
 
-/// <summary>
-///     The FitConnect API Client
-/// </summary>
-// ReSharper disable once UnusedType.Global
-public class Client {
-    internal string? ClientId;
-    internal string? ClientSecret;
-
-    public IFluentSender Sender { get; }
-
-    public IFluentSubscriber Subscriber { get; }
-    // private Routing Routing { get; }
-
+public static class Client {
+    public static ISender GetSender(FitConnectEnvironment environment, string clientId,
+        string clientSecret, ILogger? logger = null) {
+        return new Sender(environment, clientId, clientSecret, logger);
+    }
 
     /// <summary>
-    /// Constructor for the FitConnect API Client
+    /// Creates a subscriber client
     /// </summary>
-    /// <param name="environments">Choose one endpoint or create your own one</param>
-    /// <param name="clientId">Your client id</param>
-    /// <param name="clientSecret">Your client secret</param>
-    /// <param name="logger">Optional logger</param>
-    public Client(
-        FitConnectEnvironments environments,
-        string clientId,
-        string clientSecret,
+    /// <param name="environment">FIT Connect Environment</param>
+    /// <param name="clientId">Client ID</param>
+    /// <param name="clientSecret">Client Secret</param>
+    /// <param name="privateKeyDecryption">Private key for decryption</param>
+    /// <param name="privateKeySigning">Private key for signing</param>
+    /// <param name="publicKeyEncryption">Public key for encryption</param>
+    /// <param name="publicKeySignatureVerification">Public key for signature validation</param>
+    /// <param name="logger">An instance of an ILogger</param>
+    /// <returns></returns>
+    public static ISubscriber GetSubscriber(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        string privateKeyDecryption,
+        string privateKeySigning,
+        string publicKeyEncryption,
+        string publicKeySignatureVerification,
         ILogger? logger = null) {
-        ClientId = clientId;
-        ClientSecret = clientSecret;
+        return new Subscriber(environment, clientId, clientSecret, privateKeyDecryption,
+            privateKeySigning, publicKeyEncryption, publicKeySignatureVerification, logger);
+    }
+
+#if DEBUG
+    public static ISender GetSender(FitConnectEnvironment environment, string clientId,
+        string clientSecret, IContainer container) {
+        return new Sender(environment, clientId, clientSecret, container);
+    }
+
+    public static ISubscriber GetSubscriber(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        string privateKeyDecryption,
+        string privateKeySigning,
+        string publicKeyEncryption,
+        string publicKeySignatureVerification,
+        IContainer container) {
+        return new Subscriber(environment, clientId, clientSecret, privateKeyDecryption,
+            privateKeySigning, publicKeyEncryption, publicKeySignatureVerification, container);
     }
+#endif
 }
diff --git a/FitConnect/DiContainer.cs b/FitConnect/DiContainer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9d463b01d640c2145e514c2c4dd960d0966a9e34
--- /dev/null
+++ b/FitConnect/DiContainer.cs
@@ -0,0 +1,48 @@
+using System.Net;
+using Autofac;
+using FitConnect.Services;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace FitConnect;
+
+public static class DiContainer {
+    private static IContainer? _container;
+    public static IWebProxy? WebProxy { get; set; }
+
+    public static void DisposeContainer() {
+        _container?.Dispose();
+        _container = null;
+    }
+
+    public static IContainer GetContainer(FitConnectEnvironment environment,
+        ILogger? logger = null) {
+        return _container ??= BuildContainer(environment, logger);
+    }
+
+    private static IContainer
+        BuildContainer(FitConnectEnvironment environment,
+            ILogger? logger = null) {
+        var builder = new ContainerBuilder();
+
+        if (logger == null)
+            logger = NullLoggerFactory.Instance.CreateLogger("FitConnect");
+
+        builder.RegisterInstance(environment).As<FitConnectEnvironment>();
+        builder.RegisterInstance(logger).As<ILogger>();
+
+        builder.Register(c => new OAuthService(environment.TokenUrl)).As<IOAuthService>();
+        builder.Register(c => new SubmissionService(environment.SubmissionUrl[0]))
+            .As<ISubmissionService>();
+        builder.Register(c => new DestinationService(environment.SubmissionUrl[0]))
+            .As<IDestinationService>();
+        builder.Register(c => new CasesService(environment.SubmissionUrl[0]))
+            .As<ICasesService>();
+        builder.Register(c => new RouteService(environment.RoutingUrl)).As<IRouteService>();
+
+
+
+
+        return builder.Build();
+    }
+}
diff --git a/FitConnect/Encryption/FitEncryption.cs b/FitConnect/Encryption/FitEncryption.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7b0720f522dcd27a1b1ed1adf493ca3b7283542c
--- /dev/null
+++ b/FitConnect/Encryption/FitEncryption.cs
@@ -0,0 +1,136 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Cryptography;
+using System.Text;
+using FitConnect.Models.v1.Api;
+using IdentityModel;
+using Jose;
+using Microsoft.Extensions.Logging;
+using Microsoft.IdentityModel.Tokens;
+using Newtonsoft.Json;
+
+namespace FitConnect.Encryption;
+
+public class KeySet {
+    public string? PrivateKeyDecryption { get; set; }
+    public string? PrivateKeySigning { get; set; }
+    public string? PublicKeyEncryption { get; set; }
+    public string? PublicKeySignatureVerification { get; set; }
+}
+
+public class FitEncryption {
+    private readonly IEncryptor _encryptor = new JoseEncryptor();
+    private readonly ILogger? _logger;
+    private JwtHeader _jwtHeader;
+
+    internal FitEncryption(ILogger? logger) {
+        _logger = logger;
+    }
+
+    public FitEncryption(string? privateKeyDecryption, string? privateKeySigning,
+        string? publicKeyEncryption, string? publicKeySignatureVerification, ILogger? logger) :
+        this(logger) {
+        PrivateKeyDecryption = privateKeyDecryption;
+        PrivateKeySigning = privateKeySigning;
+        PublicKeyEncryption = publicKeyEncryption;
+        PublicKeySignatureVerification = publicKeySignatureVerification;
+    }
+
+    public FitEncryption(
+        KeySet keySet,
+        ILogger? logger) : this(logger) {
+        PrivateKeyDecryption = keySet.PrivateKeyDecryption;
+        PrivateKeySigning = keySet.PrivateKeySigning;
+        PublicKeyEncryption = keySet.PublicKeyEncryption;
+        PublicKeySignatureVerification = keySet.PublicKeySignatureVerification;
+    }
+
+    public string? PrivateKeyDecryption { get; set; }
+    public string? PrivateKeySigning { get; set; }
+    public string? PublicKeyEncryption { get; set; }
+    public string? PublicKeySignatureVerification { get; set; }
+
+    public (string plainText, byte[] plainBytes, byte[] tag)
+        Decrypt(string cypherText, string key) {
+        return _encryptor.Decrypt(key, cypherText);
+    }
+
+    public (string plainText, byte[] plainBytes, byte[] tag) Decrypt(string cypherText) {
+        if (PrivateKeyDecryption == null)
+            throw new InvalidOperationException("PrivateKey is not provided");
+
+        return Decrypt(cypherText, PrivateKeyDecryption);
+    }
+
+
+    public string Encrypt(string plainText, string key) {
+        return _encryptor.Encrypt(key, plainText);
+    }
+
+    public string Encrypt(string plain) {
+        if (PublicKeyEncryption == null)
+            throw new InvalidOperationException("PublicKey is not provided");
+
+        return Encrypt(plain, PublicKeyEncryption);
+    }
+
+    public string Encrypt(byte[] plain, string key) {
+        return _encryptor.Encrypt(key, plain);
+    }
+
+    public string Encrypt(byte[] plain) {
+        if (PublicKeyEncryption == null)
+            throw new InvalidOperationException("PublicKey is not provided");
+
+        return Encrypt(plain, PublicKeyEncryption);
+    }
+
+    public string CreateSecurityEventToken(string submissionId,
+        string caseId,
+        string destinationId,
+        string eventName, Problems[]? problemsArray) {
+        var signingKey = Jwk.FromJson(PrivateKeySigning, new JsonMapper());
+        var transactionId = "case:" + caseId;
+        var subject = "submission:" + submissionId;
+
+        _jwtHeader =
+            new JwtHeader(new SigningCredentials(new JsonWebKey(PrivateKeySigning), "PS512")) {
+                //{ "typ", "secevent+jwt" },
+                //  { "kid", signingKey.KeyId },
+                //  { "alg", "PS512" }
+            };
+
+        object problems = problemsArray == null ? new { } : new { problems = problemsArray };
+
+        _jwtHeader["typ"] = "secevent+jwt";
+
+        var token = new JwtSecurityToken(
+            _jwtHeader, new JwtPayload {
+                { "sub", subject },
+                { "jti", Guid.NewGuid().ToString() },
+                { "iat", DateTime.UtcNow.ToEpochTime() },
+                { "iss", destinationId }, {
+                    "events",
+                    new Dictionary<string, object> {
+                        { eventName, problems }
+                    }
+                },
+                { "txn", transactionId }
+            });
+
+        var handler = new JwtSecurityTokenHandler();
+
+
+        return handler.WriteToken(token);
+    }
+
+    public static string CalculateHash(string data) {
+        return ByteToHexString(SHA512.Create().ComputeHash(Encoding.UTF8.GetBytes(data)));
+    }
+
+    private static string ByteToHexString(IEnumerable<byte> data) {
+        var sb = new StringBuilder();
+        foreach (var b in data) sb.Append(b.ToString("x2"));
+
+        return sb.ToString();
+    }
+}
diff --git a/FitConnect/Encryption/IEncryptor.cs b/FitConnect/Encryption/IEncryptor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3af8f0f375785dd8864060560252fb0bd8ff56f8
--- /dev/null
+++ b/FitConnect/Encryption/IEncryptor.cs
@@ -0,0 +1,7 @@
+namespace FitConnect.Encryption;
+
+public interface IEncryptor {
+    public (string plainText, byte[] plainBytes, byte[] tag) Decrypt(string key, string cipher);
+    public string Encrypt(string key, string plain);
+    public string Encrypt(string key, byte[] plain);
+}
diff --git a/FitConnect/Encryption/JoseEncryptor.cs b/FitConnect/Encryption/JoseEncryptor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..91ca13ad3b441165fb71911b374561180ac55d45
--- /dev/null
+++ b/FitConnect/Encryption/JoseEncryptor.cs
@@ -0,0 +1,72 @@
+using Jose;
+
+// ReSharper disable RedundantExplicitArrayCreation
+
+namespace FitConnect.Encryption;
+
+public class JoseEncryptor : IEncryptor {
+    private const JweEncryption Encryption = JweEncryption.A256GCM;
+    private const JweCompression Compression = JweCompression.DEF;
+    private const JweAlgorithm Algorithm = JweAlgorithm.RSA_OAEP_256;
+    private const SerializationMode SerializationMode = Jose.SerializationMode.Compact;
+
+    private const string ErrorMessage =
+        "On macOS add DYLD_LIBRARY_PATH to your environment variables. Look at the README for more information.";
+
+
+    public (string plainText, byte[] plainBytes, byte[] tag)
+        Decrypt(string key, string cipher) {
+        try {
+            var jwk = Jwk.FromJson(key, new JsonMapper());
+            return Decrypt(jwk, cipher);
+        }
+        catch (PlatformNotSupportedException e) {
+            throw new PlatformNotSupportedException(ErrorMessage, e);
+        }
+    }
+
+    public string Encrypt(string key, string plain) {
+        // ti1Z0KkG1uAXvd2XnWb5wWu8slGylvsvz3nOSe7yuAc
+        try {
+            var jwk = Jwk.FromJson(key, new JsonMapper());
+            return JWE.Encrypt(plain,
+                new JweRecipient[] { new(Algorithm, jwk) },
+                Encryption,
+                compression: Compression,
+                mode: SerializationMode,
+                extraProtectedHeaders: new Dictionary<string, object> {
+                    { "kid", jwk.KeyId },
+                    { "cty", "application/json" }
+                }
+            );
+        }
+        catch (PlatformNotSupportedException e) {
+            throw new PlatformNotSupportedException(ErrorMessage, e);
+        }
+    }
+
+    public string Encrypt(string key, byte[] plain) {
+        try {
+            var jwk = Jwk.FromJson(key, new JsonMapper());
+            return JWE.EncryptBytes(plain,
+                new JweRecipient[] { new(Algorithm, jwk) },
+                Encryption,
+                compression: Compression,
+                mode: SerializationMode,
+                extraProtectedHeaders: new Dictionary<string, object> {
+                    { "kid", jwk.KeyId },
+                    { "cty", "application/json" }
+                }
+            );
+        }
+        catch (PlatformNotSupportedException e) {
+            throw new PlatformNotSupportedException(ErrorMessage, e);
+        }
+    }
+
+    private (string plainText, byte[] plainBytes, byte[] tag) Decrypt(Jwk key, string payload) {
+        var result = JWE.Decrypt(payload, key, Algorithm, Encryption);
+
+        return (result.Plaintext, result.PlaintextBytes, result.AuthTag);
+    }
+}
diff --git a/FitConnect/FitConnect.csproj b/FitConnect/FitConnect.csproj
index 25b093e1c63712f8f48c97e92133e5d0f37be8f1..3827c420f787bd35cf4c8de00b4f830dda6be88e 100644
--- a/FitConnect/FitConnect.csproj
+++ b/FitConnect/FitConnect.csproj
@@ -4,27 +4,34 @@
         <TargetFramework>net6.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
-        <AssemblyVersion>0.0.0</AssemblyVersion>
-        <FileVersion>0.0.0</FileVersion>
-        <PackageVersion>0.0.0</PackageVersion>
+        <AssemblyVersion></AssemblyVersion>
+        <FileVersion></FileVersion>
+        <PackageVersion>0.1.0-beta.1</PackageVersion>
         <Title>FitConnect SDK</Title>
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Autofac" Version="6.4.0" />
-      <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
-      <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
-      <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
-      <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.20.0" />
+        <PackageReference Include="Autofac" Version="6.4.0" />
+        <PackageReference Include="IdentityModel" Version="6.0.0" />
+        <PackageReference Include="jose-jwt" Version="4.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
+        <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.21.0" />
+        <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.21.0" />
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
+        <PackageReference Include="NJsonSchema" Version="10.7.2" />
+        <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.21.0" />
     </ItemGroup>
 
     <ItemGroup>
-      <None Remove="metadata.schema.json" />
-      <EmbeddedResource Include="metadata.schema.json" />
+        <None Remove="metadata.schema.json" />
+        <EmbeddedResource Include="metadata.schema.json" />
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\Services\Services.csproj" />
+        <Compile Remove="FunctionalBaseClass.cs" />
+        <Compile Remove="Models\OAuthAccessToken.cs" />
+        <Compile Remove="DiContainer.cs" />
     </ItemGroup>
 
 </Project>
diff --git a/FitConnect/FitConnect.csproj.DotSettings b/FitConnect/FitConnect.csproj.DotSettings
index f82351d60d72e7de02dd6aab4adca153a75297f6..d80459a6f9c67eaa547e53ce6c84c64a060debbc 100644
--- a/FitConnect/FitConnect.csproj.DotSettings
+++ b/FitConnect/FitConnect.csproj.DotSettings
@@ -1,2 +1,2 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
-	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interfaces/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/FitConnect/FitConnectClient.cs b/FitConnect/FitConnectClient.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8968ff7f4eff54900500801c3638949b3693cca1
--- /dev/null
+++ b/FitConnect/FitConnectClient.cs
@@ -0,0 +1,136 @@
+using System.Net;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Models;
+using FitConnect.Services;
+using FitConnect.Services.Interfaces;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect;
+
+public abstract class FitConnectClient {
+    private readonly string? _privateKeyDecryption;
+    private readonly string? _privateKeySigning;
+    private readonly string? _publicKeyEncryption;
+    private readonly string? _publicKeySignatureVerification;
+
+
+    protected FitConnectClient(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        ILogger? logger = null,
+        string? privateKeyDecryption = null,
+        string? privateKeySigning = null,
+        string? publicKeyEncryption = null,
+        string? publicKeySignatureVerification = null
+    ) : this() {
+        _privateKeyDecryption = privateKeyDecryption;
+        _privateKeySigning = privateKeySigning;
+        _publicKeyEncryption = publicKeyEncryption;
+        _publicKeySignatureVerification = publicKeySignatureVerification;
+        OAuthService = new OAuthService(environment.TokenUrl, "V1", clientId, clientSecret, logger);
+        SubmissionService =
+            new SubmissionService(environment.SubmissionUrl[0], OAuthService, logger: logger);
+        RouteService = new RouteService(environment.RoutingUrl, OAuthService, logger: logger);
+        CasesService = new CasesService(environment.SubmissionUrl[0], OAuthService, logger: logger);
+        DestinationService =
+            new DestinationService(environment.SubmissionUrl[0], OAuthService, logger: logger);
+        Logger = logger;
+    }
+
+    protected FitConnectClient(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        IContainer container,
+        string? privateKeyDecryption = null,
+        string? privateKeySigning = null,
+        string? publicKeyEncryption = null,
+        string? publicKeySignatureVerification = null
+    ) : this() {
+        _privateKeyDecryption = privateKeyDecryption;
+        _privateKeySigning = privateKeySigning;
+        _publicKeyEncryption = publicKeyEncryption;
+        _publicKeySignatureVerification = publicKeySignatureVerification;
+        OAuthService = container.Resolve<IOAuthService>();
+        SubmissionService = container.Resolve<ISubmissionService>();
+        RouteService = container.Resolve<IRouteService>();
+        CasesService = container.Resolve<ICasesService>();
+        DestinationService = container.Resolve<IDestinationService>();
+        Logger = container.Resolve<ILogger>();
+    }
+
+
+    private FitConnectClient() {
+        Encryption = new FitEncryption(Logger);
+    }
+
+    public IOAuthService OAuthService { get; }
+    public ISubmissionService SubmissionService { get; }
+    public IRouteService RouteService { get; }
+    public IDestinationService DestinationService { get; }
+    public ICasesService CasesService { get; }
+    public ILogger? Logger { get; }
+    public FitEncryption Encryption { get; set; }
+
+    public void SetProxy(WebProxy proxy) {
+        OAuthService.Proxy = proxy;
+        SubmissionService.Proxy = proxy;
+        DestinationService.Proxy = proxy;
+        RouteService.Proxy = proxy;
+    }
+
+    public WebProxy? GetProxy() {
+        return OAuthService.Proxy;
+    }
+
+
+    /// <summary>
+    ///     Retrieve the events for the submission
+    /// </summary>
+    /// <param name="caseId"></param>
+    /// <returns></returns>
+    public List<SecurityEventToken> GetStatusForSubmission(string caseId) {
+        var events = SubmissionService.GetStatusForSubmissionAsync(caseId).Result;
+        return events.Select(e => new SecurityEventToken(e)).ToList();
+    }
+
+    /// <summary>
+    ///     Encrypt attachments (Anhänge)
+    /// </summary>
+    /// <param name="publicKey"></param>
+    /// <param name="attachment"></param>
+    /// <returns></returns>
+    public KeyValuePair<string, string> Encrypt(string publicKey, Attachment attachment) {
+        var content = Encryption.Encrypt(attachment.Content, publicKey);
+        return new KeyValuePair<string, string>(attachment.Id, content);
+    }
+
+    /// <summary>
+    ///     Encrypt attachments (Anhänge)
+    /// </summary>
+    /// <param name="publicKey">Public key for encryption</param>
+    /// <param name="attachments">List of attachments to encrypt</param>
+    /// <returns></returns>
+    public Dictionary<string, string> Encrypt(string publicKey,
+        IEnumerable<Attachment> attachments) {
+        return attachments.Select(a => Encrypt(publicKey, a))
+            .ToDictionary(p => p.Key, p => p.Value);
+    }
+}
+
+public static class FitConnectClientExtensions {
+    /// <summary>
+    /// </summary>
+    /// <param name="host"></param>
+    /// <param name="port"></param>
+    /// <param name="username"></param>
+    /// <param name="password"></param>
+    /// <returns></returns>
+    public static T WithProxy<T>(this T caller, string host, int port, string? username = null,
+        string? password = null) where T : FitConnectClient {
+        var proxy = new WebProxy(host, port);
+        if (username != null && password != null)
+            proxy.Credentials = new NetworkCredential(username, password);
+
+        caller.SetProxy(proxy);
+        return caller;
+    }
+}
diff --git a/FitConnect/FunctionalBaseClass.cs b/FitConnect/FunctionalBaseClass.cs
deleted file mode 100644
index e9cb4b2de5a4df239d7e6cb5f2f8cd839808f50e..0000000000000000000000000000000000000000
--- a/FitConnect/FunctionalBaseClass.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using System.Net.Http.Headers;
-using System.Net.Http.Json;
-using System.Security.Cryptography;
-using System.Text;
-using FitConnect.Models;
-using Microsoft.Extensions.Logging;
-
-namespace FitConnect;
-
-public class FitConnectEnvironments {
-    /// <summary>
-    /// Default constructor.
-    /// </summary>
-    /// <param name="tokenUrl">URL for receiving the OAuth token</param>
-    /// <param name="submissionApi">URL for the submission API</param>
-    /// <param name="routingApi">URL for the routing API</param>
-    public FitConnectEnvironments(string tokenUrl, string[] submissionApi, string routingApi) {
-        TokenUrl = tokenUrl;
-        SubmissionApi = submissionApi;
-        RoutingApi = routingApi;
-    }
-
-    /// <summary>
-    /// URL for receiving the OAuth token.
-    /// </summary>
-    public string TokenUrl { get; }
-
-    /// <summary>
-    /// URL for the submission API.
-    /// </summary>
-    public string[] SubmissionApi { get; }
-
-    /// <summary>
-    /// URL for the routing API.
-    /// </summary>
-    public string RoutingApi { get; }
-
-
-    public enum EndpointType {
-        Development,
-        Testing,
-        Production
-    }
-
-    /// <summary>
-    /// Creates the endpoints for the given environment.
-    /// </summary>
-    /// <param name="endpointType">Environment to get endpoints for</param>
-    /// <returns></returns>
-    /// <exception cref="ArgumentException">Not all environments are ready to use</exception>
-    public static FitConnectEnvironments Create(EndpointType endpointType) {
-        return endpointType switch {
-            EndpointType.Development => DevEnvironments,
-            EndpointType.Testing => throw new ArgumentException("Not approved for online testing"),
-            EndpointType.Production => throw new ArgumentException("NOT PRODUCTION READY"),
-            _ => throw new ArgumentOutOfRangeException(nameof(endpointType), endpointType, null)
-        };
-    }
-
-    private static readonly FitConnectEnvironments DevEnvironments = new(
-        "https://auth-testing.fit-connect.fitko.dev/token",
-        new []{"https://submission-api-testing.fit-connect.fitko.dev"},
-        "https://routing-api-testing.fit-connect.fitko.dev"
-    );
-
-    private static readonly FitConnectEnvironments TestEnvironments = new(
-        "https://auth-testing.fit-connect.fitko.dev/token",
-        new []{"https://submission-api-testing.fit-connect.fitko.dev"},
-        "https://routing-api-testing.fit-connect.fitko.dev"
-    );
-
-    private static readonly FitConnectEnvironments ProductionEnvironments = new(
-        "https://auth-testing.fit-connect.fitko.dev/token",
-        new []{"https://submission-api-testing.fit-connect.fitko.dev"},
-        "https://routing-api-testing.fit-connect.fitko.dev"
-    );
-}
-
-public abstract class FunctionalBaseClass {
-    protected readonly ILogger? logger;
-    private RSA _rsa;
-    public FitConnectEnvironments Environments { get; }
-
-    protected FunctionalBaseClass(ILogger? logger, FitConnectEnvironments? endpoints) {
-        Environments = endpoints ??
-                    FitConnectEnvironments.Create(FitConnectEnvironments.EndpointType.Development);
-
-        this.logger = logger;
-    }
-
-
-    /// <summary>
-    /// Requesting an OAuth token from the FitConnect API.
-    /// 
-    /// You can get the Client ID and Client Secret from the FitConnect Self Service portal
-    /// under https://portal.auth-testing.fit-connect.fitko.dev
-    /// </summary>
-    /// <param name="clientId">Your client Id</param>
-    /// <param name="clientSecret">Your client Secret</param>
-    /// <param name="scope">Scope if needed</param>
-    /// <returns></returns>
-    public async Task<OAuthAccessToken?> GetTokenAsync(string clientId, string clientSecret,
-        string? scope = null) {
-        var client = new HttpClient();
-        client.DefaultRequestHeaders.Accept.Add(
-            MediaTypeWithQualityHeaderValue.Parse("application/json"));
-
-        var requestContent = new Dictionary<string, string> {
-            { "grant_type", "client_credentials" },
-            { "client_id", clientId },
-            { "client_secret", clientSecret }
-        };
-
-        if (scope != null)
-            requestContent["scope"] = scope;
-
-        var content = new FormUrlEncodedContent(requestContent);
-
-        var request = new HttpRequestMessage(HttpMethod.Post, Environments.TokenUrl) {
-            Content = content,
-            Method = HttpMethod.Post
-        };
-
-        var response = await client.SendAsync(request);
-
-        return await response.Content.ReadFromJsonAsync<OAuthAccessToken>();
-    }
-
-    public Task<SecurityEventToken> GetSetDataAsync() {
-        throw new NotImplementedException();
-    }
-
-    public byte[] EncryptDataAsync(byte[] data,
-        byte[]? publicKey,
-        string? password,
-        int numberOfIterations) {
-        _rsa = RSA.Create(2048);
-        // _rsa.ImportRSAPublicKey(publicKey, out var read);
-
-        logger?.LogInformation(
-            "Encrypting data with public key: {}",
-            Convert.ToBase64String(_rsa.ExportRSAPublicKey()));
-
-        // var keyParams = new PbeParameters(
-        //     PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, numberOfIterations);
-        //
-        // var privateKey =
-        //     rsa.ExportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(password), keyParams);
-        //
-        // logger?.LogInformation(
-        //     "Private key: {}", privateKey);
-
-        return _rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
-    }
-
-    public byte[] DecryptDataAsync(byte[] data, byte[] privateKey) {
-        return _rsa.Decrypt(data, RSAEncryptionPadding.OaepSHA256);
-    }
-
-
-    private async Task<T?> RestCall<T>(Uri uri, HttpMethod method, string body) {
-        var client = new HttpClient();
-        client.DefaultRequestHeaders.Add("Accept", "application/json");
-        client.DefaultRequestHeaders.Add("Content-Type", "application/json");
-
-        var request = new HttpRequestMessage();
-        request.Method = method;
-        request.Content = new StringContent(body, Encoding.UTF8, "application/json");
-        request.RequestUri = uri;
-
-        var response = await client.SendAsync(request);
-
-        if (response.IsSuccessStatusCode)
-            return (await response.Content.ReadFromJsonAsync<T>());
-
-        throw new HttpRequestException("Error calling FitConnect API");
-    }
-}
diff --git a/FitConnect/HttpCalls/OAuthToken.http b/FitConnect/HttpCalls/OAuthToken.http
new file mode 100644
index 0000000000000000000000000000000000000000..2f94b0f5eb0d9af3cb0100c270efe7d175242ba5
--- /dev/null
+++ b/FitConnect/HttpCalls/OAuthToken.http
@@ -0,0 +1,12 @@
+### Getting the data from the database BEARER
+POST {{oauth_api_url}}/token
+Content-Type: application/x-www-form-urlencoded
+Accept: application/json
+
+grant_type=client_credentials&client_id={{senderId}}&client_secret={{senderSecret}}
+
+> {%
+ client.global.set("token", response.body.access_token);
+ // client.global.scope = response.body['scope']
+ %}
+
diff --git a/FitConnect/HttpCalls/RoutingApi.http b/FitConnect/HttpCalls/RoutingApi.http
new file mode 100644
index 0000000000000000000000000000000000000000..ca26f34339125366d1cef16c841c87716c31ba9f
--- /dev/null
+++ b/FitConnect/HttpCalls/RoutingApi.http
@@ -0,0 +1,8 @@
+### Get areas
+GET {{routing_api_url}}/v1/areas?areaSearchexpression=93437
+Accept: application/json
+
+### Get areas
+GET {{routing_api_url}}/v1/routes?leikaKey=99012070006000
+Accept: application/json
+
diff --git a/FitConnect/HttpCalls/SenderCalls.http b/FitConnect/HttpCalls/SenderCalls.http
new file mode 100644
index 0000000000000000000000000000000000000000..6834bb4b14fef316283551bb70a4897c2edba7aa
--- /dev/null
+++ b/FitConnect/HttpCalls/SenderCalls.http
@@ -0,0 +1,67 @@
+### Get Destination infos
+GET {{submission_api_url}}/v1/destinations/{{destinationId}}
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+> {% 
+ client.global.set('keyId', response.body.encryptionKid);
+ %}
+
+### Get Destination infos
+GET {{submission_api_url}}/v1/destinations/{{destinationId}}/keys/{{keyId}}
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+
+### Create submission
+POST {{submission_api_url}}/v1/submissions
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+{
+    "destinationId": "{{destinationId}}",
+    "announcedAttachments": [
+        "12399641-2067-4e8b-b049-91b2a6c90544"
+    ],
+    "serviceType": {
+        "name": "Antrag Name",
+        "identifier": "urn:de:fim:leika:leistung:99102013104000"
+    },
+    "callback": {
+        "url": "https://my-onlineservice.example.org/callbacks/fit-connect",
+        "secret": "insecure_unsafe_qHScgrg_kP-R31jHUwp3GkVkGJolvBchz65b74Lzue0"
+    }
+}
+
+> {% 
+ client.global.set('caseId', response.body.caseId);
+ client.global.set('submissionId', response.body.submissionId);
+ %}
+
+### GET state of Submission
+GET {{submission_api_url}}/v1/cases/{{caseId}}/events
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+
+### GET state of Submission
+GET {{submission_api_url}}/v1/cases/4b491eac-2ca0-4b45-94c0-0206b736f9e6/events
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+
+### Submit submission
+PUT {{submission_api_url}}/v1/submissions/{{submissionId}}
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+{
+  "encryptedMetadata": "l3--gxZp131zyqCOWmdzxHiNySKGSlyOI63eIBoPDg1-f9v-GPFLCjNANFbW8y96orN0RYLsWQEa4JAV93lvz_caJQXMyFiE8VdRr7SjS4ivBfyizdplnnBQ2V_vZCL7t65GneogIF-xpiXF4zxOH6dY5BJxq9IC-6EkWK76fSa5xfe2k-SXDQXlqP6fWqkR2iECKO16cY5rrneMzkvC9nPNzeMKtlcnirQfKPEklK5Kp_6gLRT8nQrBUma5bDhctpOweSIiVeuv_zgHOzvxvuK6_Xoc1M0WHniR4y_qUA8YzLUI1-FfMu1N88TI0brcngbF4zVarQXv7q8gzGynEgTln11JrL0Fx87U_-Gfc5RUsjxMynEgZ8Qr72UpYF3HcMEg4xRTeivlWiOu4s9hvSyE2RJanp6amB68CPG-TUaS5tKbEPjHc5zmTOedUAgw-mfLOJNAleW_J-7L07pWCYxR0Jr5hVMFBBCuh1mbeMoW1kF1mfmDwT_X6U-cC_srvXQiSVk7kub0NJRpG9mx6FvHapVZRO709Luslj4Fw9d8Jnw6zw_MAxbBPOvNAVVm20Nqm66_aB4wv-qJDWA3SQZFW5dO4Zuh8eubLhOBes0jHTEFQSh6q4r1zVNYC1nSJUYzYpK9eaNa43oXF-HVp4rdXNaTyE6M0Vd5YgZwbOLrVS4QyvvURMgOcD5xRQCi6F89ZrAN09aoCAeaKuB-r5VuDfquYB5J6RMSXNLvXuHOR1iHwcC6EFvNnqzDystsDXJudPLSdiQJBszpOjM28DZgtl-I0hw7-FVKdTWlkB9IpbjTNMOdJcWMUCpoJIkAIiWYlAsmZ0K22mj_fFY08kPKyuEOJNBB_dDf2O_kF92PNY51I1Npa_PE2uXEaBp95772KvSdvn6fDri42iGJGIGS5cADQDHiDTYGrBsluzdLD6PKD4XBrGipM9jkWbTRNG8lY4lkNtUwtKU5rVDcbdstAivwdlTpeLKHVjPE-ITW-5QhM13HCgZ6eJPFpwpT4VpAUT4euikzoRccyJOYqkzKr1Z0JP-7kop-0GnKNZ1zBJEaEq_gf53lYInrV9f3EQM8ynQpp_0wMNwVU6_0xOjEMX5DMt2io_whkNEvuTa56A"
+}
+
diff --git a/FitConnect/HttpCalls/StaticTests.http b/FitConnect/HttpCalls/StaticTests.http
new file mode 100644
index 0000000000000000000000000000000000000000..f2996b8a2a3a4bff6684e2cc0d09e9b4b8128824
--- /dev/null
+++ b/FitConnect/HttpCalls/StaticTests.http
@@ -0,0 +1,24 @@
+### Getting the data from the database BEARER
+POST {{oauth_api_url}}/token
+Content-Type: application/x-www-form-urlencoded
+Accept: application/json
+
+grant_type=client_credentials&client_id={{senderId}}&client_secret={{senderSecret}}
+
+> {%
+ client.global.set("token", response.body.access_token);
+ // client.global.scope = response.body['scope']
+ %}
+
+
+
+### Submit submission
+PUT {{submission_api_url}}/v1/submissions/d698150c-0792-4be7-aa42-580fb0774f02
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{token}}
+
+{
+  "encryptedMetadata": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJjdHkiOiJhcHBsaWNhdGlvbi9qc29uIiwiZW5jIjoiQTI1NkdDTSIsImtpZCI6InZyN2NXS0dsLWc0V2FfQ1JHb3dOaEFZV19nUWItYWtNYmlpZ3hOMEVrREkiLCJ6aXAiOiJERUYifQ.MMPBbWa7gnK-bu2Iocp0WVT7IeVqpwdV23cH3t9qGmkARXLF14bVQh8oT0wIaZRhn0DLUEwv9tsQch7oZvOj4KZdyip9F-cvt4Z0re74nzC1pC9FGGVdUmOWOiOeh8KSnpP6iVWUlA5HVotao0yOt6OqeqdnbgSt_MT7ARPURAvqvTCTgZNtY3_dBKhm0AQMyaGlQdMZitogXNgXXiXPbydWpgoLvOEB_Y43cIPdqBN1iK1zhuwHHeXVSfxCtYNOkh_mqSAvDy-lHK9rYschhjhynOBEQYzFv66tiCn7HjJpSoFY2XaOM07PYy45dMUEzQv_bkQlnlNhAn58K_FUvwsT0BhRX12pq27Xpg6X7qa4KYrxByKHsiPSegJtsIMT-24BSU8vU3KEEqIvwlp5XtPgPCi4mrzU_wqGozxlbrAlT59mvcxhd3LUQWBjd5EByIZZXRkq9b2jvsiauZahzUppMX8r5iswrlteB7SkVJUMVA3_uGPFPfxGyIBAykS28DB7i9M-_MIoEEkT07xNMHApU9Q95I02c8q9dnQb146ZwqlQ8oGepparaCQrUVIIjD--6oh67ta5M2e1NesK_iWEgzTEOsh_ymyv7TFNmTO9iMQ7HuK8Mf6tozyqruzAHTqiPcd9DdrO3TJMrTyYIXQSnsFz_uyxJ_09bcVDD4M.WNkZENRbSaFdfaPA.YVVkbO3aHaTiNbRO33F2R99gh0PAMyUV6Az_q_Po7mUFTXfvgdRI__VPjYVlktRkkZXmQs13n8BVLbLGH_OkobOvLD3Ufxdn8z0Lot3f_ydXOGsdc1ID-OqAx3mhxzrQi9JdO4QYSWtCIHXi96iyyljQmVwj27ioukNOLbfiH2WVjXR1m7v92ePc8FLDNUMh-epXYk-lSyV3T5w1xRXljZrS53ictqFXrQd31crDVfbCilXwlndYLJCp7NvvyoprtOZIEXsUpiFLpOT3du5PhNrPoAXPbjpYFAKLaI3F56vaSwqF820NJlJWXyRE18m8MVpELRllPUl1D96w7-YnsUbsMWgB2rqoPS8ArzbkIpEa-CIpgnNVTEptnFLAye3Qnb9kCnkjQNdE8AfMfneOcW2Jdox2hwM178tqR5jxxRN3sucfqQ_vuqSo3CcLgnFnYP1GpYBdSv_ehus4VrMEtjRtp5rjjpnqUYydyP3JX7FSUmvxYigq5_-yO24UZQLozcnwL04_oMMbqPfMsiyY_GOS-jA5xl4Qt6wooixoH1nQgJpcVvIniaRdZ7oz3uUhmNVxB5aSkC8UO9KCtp2Warg.VajPcUlW2bShHc09-7KQ8Q",
+  "encryptedData": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJjdHkiOiJhcHBsaWNhdGlvbi9qc29uIiwiZW5jIjoiQTI1NkdDTSIsImtpZCI6InZyN2NXS0dsLWc0V2FfQ1JHb3dOaEFZV19nUWItYWtNYmlpZ3hOMEVrREkiLCJ6aXAiOiJERUYifQ.qbMW9T1LYYA1jmcjWZi0AYiUrg_124Fk8Hmfx6Fjt6usv0PKHLzMHt3Z7j_yKr9nSLbosoKB6a5FwjgZvkaq_ffuPdCQt36YIGoEdpxP2y3lkEdx8662S-wVKpjukRJi1vFK5Ymq6W6bk2Tsh2GAgwkXr_7q_WjIzRtBRrVmzmHjoDB8jUrQv9_7yQJy-1lCs0_efhmxZPHeHTmOgkHy8w-xAhjVlhGH2ErgFeUZESGjHGGKVg3kancxeNpLz0hIRAej2C8M7QKMTpcuim_J0M70vOK5VFDcDeYxJSUaaIXGMYv2-foISVAA5Dp0t5BZcQTNUT5MbMGBTGcFnICaBEEAef5jWG6320heQavyDLpdS96ddCuWRGIJHlG0wLORPuJqR47kxfR_9BPc2eYJEG8bEFeyPmDQ83Qqi8JTX5Tz1mLGHyUIA8y_tl3qTV1emc6MAAeG-TWTmrds5msjEWaZ3Q9Ee3m55b0PKVMR8lT5P_i0amhZwQIVUXmZYwRepXqXhezJrF-G2Pltu7ge83nCu7i9a5q740sR25cKv3qv3KLBgi-mliSFswTP1rAOCs_UkjN1GmOOQD0mgtPpuY_-yAhl2XOOBgfkHcDKzGP4abTCsEabAaQlbe3bwIKmh2IJuPfPB-YbT7cdrEbJdRo2YBsJksAqm_mDRbLqCtA.fdb92PBWLFD6m8iw.Yxo8kvGgCEgJL-MVWtu64b5Bc0OxlsNedNmCcwCsQXiCuqkucNWxHacpk7AbOr_3ik_8IxFFRt0gnRYXfiiUuKLH1kvuePgY_eQY.iJ3f1rHtnDxvG2qz_snZ-A"
+}
diff --git a/FitConnect/HttpCalls/SubscriberCalls.http b/FitConnect/HttpCalls/SubscriberCalls.http
new file mode 100644
index 0000000000000000000000000000000000000000..ed3b6888221967d812277576b5f191fe77ec7b4b
--- /dev/null
+++ b/FitConnect/HttpCalls/SubscriberCalls.http
@@ -0,0 +1,39 @@
+### Get Token
+POST {{oauth_api_url}}/token
+Content-Type: application/x-www-form-urlencoded
+Accept: application/json
+
+grant_type=client_credentials&client_id={{subscriberId}}&client_secret={{subscriberSecret}}
+
+> {%
+ client.global.set("subscriber_token", response.body.access_token);
+ // client.global.scope = response.body['scope']
+ %}
+
+
+
+### GET Avaliable submissions
+GET {{submission_api_url}}/v1/submissions
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{subscriber_token}}
+
+### GET Avaliable submissions
+GET {{submission_api_url}}/v1/submissions/4d0da617-40ab-4e8b-8c20-2aa7024bb2e7
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{subscriber_token}}
+
+
+### GET Avaliable submissions
+GET {{submission_api_url}}/v1/submissions/7d183fc1-e0e4-48bb-ae1d-c9522dea9591
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer {{subscriber_token}}
+
+
+### GET Attachment
+GET {{submission_api_url}}/v1/submissions/303c60d3-3f6f-4913-bb51-4e66fe133c7f/attachments/27774bc8-4c37-4df0-866b-5be1f6a3eefb
+Accept: application/jose
+Content-Type: application/jose
+Authorization: Bearer {{subscriber_token}}
diff --git a/FitConnect/HttpCalls/http-client.env.json b/FitConnect/HttpCalls/http-client.env.json
new file mode 100644
index 0000000000000000000000000000000000000000..d4a228a07c5b955ad9aec3e168b996ae6e767285
--- /dev/null
+++ b/FitConnect/HttpCalls/http-client.env.json
@@ -0,0 +1,13 @@
+{
+  "dev": {
+    "routing_api_url": "https://routing-api-testing.fit-connect.fitko.dev",
+    "submission_api_url": "https://submission-api-testing.fit-connect.fitko.dev",
+    "oauth_api_url": "https://auth-testing.fit-connect.fitko.dev",
+    "keyId": "",
+    "destinationId": "aa3704d6-8bd7-4d40-a8af-501851f93934",
+    "senderId": "73a8ff88-076b-4263-9a80-8ebadac97b0d",
+    "senderSecret": "rdlXms-4ikO47AbTmmCTdzFoE4cTSt13JmSbcY5Dhsw",
+    "subscriberId": "20175c2b-c4dd-4a01-99b1-3a08436881a1",
+    "subscriberSecret": "KV2qd7qc5n-xESB6dvfrTlMDx2BWHJd5hXJ6pKKnbEQ"
+  }
+}
\ No newline at end of file
diff --git a/FitConnect/Interfaces/IBaseFunctionality.cs b/FitConnect/Interfaces/IBaseFunctionality.cs
deleted file mode 100644
index 14665ab9662ba3662f28bca67de41159ec4162c0..0000000000000000000000000000000000000000
--- a/FitConnect/Interfaces/IBaseFunctionality.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Threading.Tasks;
-using FitConnect.Models;
-
-namespace FitConnect;
-
-public interface IBaseFunctionality {
-    Task<OAuthAccessToken> GetTokenAsync(string clientId, string clientSecret,
-        string? scope = null);
-
-    Task<SecurityEventToken> GetSetDataAsync();
-
-    // Receive SecurityEventToken and check signature
-}
\ No newline at end of file
diff --git a/FitConnect/Interfaces/IFluentApi.cs b/FitConnect/Interfaces/IFluentApi.cs
deleted file mode 100644
index 3205566d23fe10946a9741c7ecedf8b7ca5a6d32..0000000000000000000000000000000000000000
--- a/FitConnect/Interfaces/IFluentApi.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System;
-using System.Collections.Generic;
-using FitConnect.Models;
-
-namespace FitConnect;
-
-public interface IFluentSender {
-    /// <summary>
-    /// Configures the client for the given destination and loads the public key
-    /// </summary>
-    /// <param name="destinationId">unique identifier of the clients destination</param>
-    /// <returns>the upload step for attachments</returns>
-    public IFluentSenderWithDestination WithDestination(string destinationId);
-
-    public IFluentSenderWithData WithData(string data);
-
-    /// <summary>
-    /// Send submission to FIT-Connect API.
-    /// </summary>
-    /// <returns>submitted submission</returns>
-    public Submission Submit();
-}
-
-public interface IFluentSenderWithDestination {
-    /// <summary>
-    /// Sends the submission with a list of attachments
-    /// </summary>
-    /// <param name="attachments">that are sent with the submission</param>
-    /// <returns>the step where additional data can be added to the submission</returns>
-    public IFluentSenderWithAttachments WithAttachments(IEnumerable<Attachment> attachments);
-}
-
-public interface IFluentSenderWithAttachments : IFluentSenderReady {
-    /// <summary>
-    /// Data as string.
-    /// </summary>
-    /// <param name="data">json or xml as string</param>
-    /// <returns>next step to submit the data</returns>
-    public IFluentSenderWithData WithData(string data);
-}
-
-public interface IFluentSenderWithData : IFluentSenderReady {
-}
-
-public interface IFluentSenderReady {
-    /// <summary>
-    /// Send submission to FIT-Connect API.
-    /// </summary>
-    /// <returns>submitted submission</returns>
-    public IFluentSender Submit();
-}
-
-public interface IFluentSubscriber {
-    /// <summary>
-    /// Loads a list of available Submissions that were submitted to the subscriber.
-    /// </summary>
-    /// <returns>List of available submissions for pickup</returns>
-    public IEnumerable<string> GetAvailableSubmissions(string? destinationId = null);
-
-    /// <summary>
-    /// Loads a single Submission by id.
-    /// </summary>
-    /// <param name="submissionId">unique identifier of a <see cref="Submission"/></param>
-    /// <returns></returns>
-    public IFluentSubscriberWithSubmission RequestSubmission(string submissionId);
-}
-
-public interface IFluentSubscriberWithSubmission {
-    public Submission Submission { get; }
-
-    /// <summary>
-    /// Loads the <see cref="Attachment"/>s for the given <see cref="Submission"/>.
-    /// </summary>
-    /// <param name="canSubmitSubmission">Function that returns a boolean if the <see cref="Submission"/> can be confirmed</param>
-    /// <returns></returns>
-    public IFluentSenderWithAttachments GetAttachments(
-        Func<IEnumerable<Attachment>, bool> canSubmitSubmission);
-}
diff --git a/FitConnect/Interfaces/ISender.cs b/FitConnect/Interfaces/ISender.cs
deleted file mode 100644
index ed47036ac5e3501093bb6a180658c06e74e3ffe3..0000000000000000000000000000000000000000
--- a/FitConnect/Interfaces/ISender.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Threading.Tasks;
-
-namespace FitConnect;
-
-public interface ISender : IBaseFunctionality {
-    // Check public keys
-    Task<bool> CheckPublicKeyAsync(string publicKey);
-
-    // Encrypt Data (Fachdaten)
-    byte[] EncryptDataAsync(string data, string publicKey);
-
-    // Encrypt attachments (Anhänge)
-    Task<string> EncryptAttachmentAsync(string attachment, string publicKey);
-
-    // Create Metadata incl. Hash
-    Task<string> CreateMetadataAsync(string data, string attachment, string publicKey);
-}
\ No newline at end of file
diff --git a/FitConnect/Interfaces/ISubscriber.cs b/FitConnect/Interfaces/ISubscriber.cs
deleted file mode 100644
index 6456dab3cceef9c0b81a6a15d5f0df22fab547b6..0000000000000000000000000000000000000000
--- a/FitConnect/Interfaces/ISubscriber.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Threading.Tasks;
-using FitConnect.Models;
-
-namespace FitConnect;
-
-public interface ISubscriber : IBaseFunctionality {
-    /// <summary>
-    /// Decrypt Data (Fachdaten)
-    /// </summary>
-    /// <param name="data">(Fachdaten)</param>
-    /// <param name="privateKey">Your private key for decryption</param>
-    /// <returns></returns>
-    Task<string> DecryptDataAsync(string data, string privateKey);
-
-    // Decrypt attachments (Anhänge)
-    Task<string> DecryptAttachmentAsync(string attachment, string privateKey);
-
-    /// <summary>
-    /// Checks the validity of the given metadata against the schema.
-    /// </summary>
-    /// <param name="jsonMetaData">JSON meta data</param>
-    /// <returns></returns>
-    Task<bool> CheckMetadataAsync(string jsonMetaData);
-
-    // Check Hash from Metadata
-    Task<bool> CheckHashAsync(string metadata);
-
-    // Create SecurityEventToken and signature
-    Task<SecurityEventToken> CreateSecurityEventTokenAsync(string data, string attachment,
-        string privateKey);
-}
\ No newline at end of file
diff --git a/FitConnect/Interfaces/Sender/ISender.cs b/FitConnect/Interfaces/Sender/ISender.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2e6824ef707adb7cf146490925244910d6b656d7
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISender.cs
@@ -0,0 +1,46 @@
+using FitConnect.Interfaces.Subscriber;
+using FitConnect.Models;
+using FitConnect.Services.Interfaces;
+
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISender : IFitConnectClient {
+    public string PublicKey { get; }
+
+    /// <summary>
+    ///     Determines the destination id and configures the sender.
+    /// </summary>
+    /// <param name="leiaKey"></param>
+    /// <param name="ags"></param>
+    /// <param name="ars"></param>
+    /// <param name="areaId"></param>
+    /// <returns></returns>
+    public ISenderWithDestination FindDestinationId(string leiaKey, string? ags = null,
+        string? ars = null,
+        string? areaId = null);
+
+    /// <summary>
+    ///     Configures the client for the given destination and loads the public key
+    /// </summary>
+    /// <param name="destinationId">
+    ///     unique identifier of the clients destination
+    ///     <para>eg: 00000000-0000-0000-0000-000000000000</para>
+    /// </param>
+    /// <returns>the upload step for attachments</returns>
+    public ISenderWithDestination WithDestination(string destinationId);
+
+
+    /// <summary>
+    /// Finding areas for the filter with paging
+    /// </summary>
+    /// <param name="filter">Search string for the area, use * as wildcard</param>
+    /// <param name="totalCount">out var for the total count</param>
+    /// <param name="offset"></param>
+    /// <param name="limit"></param>
+    /// <example>
+    /// var areas = GetAreas("Erlang*", out var _, 0, 10)
+    /// </example>
+    /// <returns></returns>
+    public IEnumerable<Area> GetAreas(string filter, out int totalCount, int offset = 0,
+        int limit = 100);
+}
diff --git a/FitConnect/Interfaces/Sender/ISenderReady.cs b/FitConnect/Interfaces/Sender/ISenderReady.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d51808023ec1b33de2e957d674adb37db27c57d2
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISenderReady.cs
@@ -0,0 +1,10 @@
+using FitConnect.Models;
+
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISenderReady {
+    /// <summary>
+    ///     Send submission to FIT-Connect API.
+    /// </summary>
+    public Submission Submit();
+}
diff --git a/FitConnect/Interfaces/Sender/ISenderWithAttachments.cs b/FitConnect/Interfaces/Sender/ISenderWithAttachments.cs
new file mode 100644
index 0000000000000000000000000000000000000000..073048275129459bf5485962d2759d684ae4e71c
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISenderWithAttachments.cs
@@ -0,0 +1,17 @@
+using FitConnect.Models;
+
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISenderWithAttachments : ISenderReady {
+    public Submission Submission { get; }
+
+    /// <summary>
+    ///     Data as string.
+    /// </summary>
+    /// <param name="data">json or xml as string</param>
+    /// <example>
+    ///     .WithData(@"{ ""name"": ""John Doe"" }")
+    /// </example>
+    /// <returns>next step to submit the data</returns>
+    public ISenderWithData WithData(string data);
+}
diff --git a/FitConnect/Interfaces/Sender/ISenderWithData.cs b/FitConnect/Interfaces/Sender/ISenderWithData.cs
new file mode 100644
index 0000000000000000000000000000000000000000..567bf942ded1cbb23a9400aa107934010ba71c44
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISenderWithData.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISenderWithData : ISenderReady {
+}
diff --git a/FitConnect/Interfaces/Sender/ISenderWithDestination.cs b/FitConnect/Interfaces/Sender/ISenderWithDestination.cs
new file mode 100644
index 0000000000000000000000000000000000000000..247da7e20f18b05b360ddff2d01e7aca8881ebf8
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISenderWithDestination.cs
@@ -0,0 +1,18 @@
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISenderWithDestination {
+    /// <summary>
+    ///     Adds the service description to the sender.
+    /// </summary>
+    /// <param name="serviceName">Name of the destination service</param>
+    /// <param name="leikaKey">
+    ///     Leika key to specify the purpose of the submission
+    ///     <para>
+    ///         eg:  urn:de:fim:leika:leistung:00000000000000
+    ///     </para>
+    /// </param>
+    /// <returns></returns>
+    ISenderWithService WithServiceType(string serviceName, string leikaKey);
+    
+
+}
diff --git a/FitConnect/Interfaces/Sender/ISenderWithService.cs b/FitConnect/Interfaces/Sender/ISenderWithService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e86042f50a9534fa7f464d2511b0b01463b33b22
--- /dev/null
+++ b/FitConnect/Interfaces/Sender/ISenderWithService.cs
@@ -0,0 +1,21 @@
+using FitConnect.Models;
+
+namespace FitConnect.Interfaces.Sender;
+
+public interface ISenderWithService {
+    public string PublicKey { get; set; }
+
+    /// <summary>
+    ///     Sends the submission with a list of attachments
+    /// </summary>
+    /// <param name="attachments">that are sent with the submission</param>
+    /// <returns>the step where additional data can be added to the submission</returns>
+    public ISenderWithAttachments WithAttachments(IEnumerable<Attachment> attachments);
+    
+    /// <summary>
+    ///     Sends the submission with a list of attachments
+    /// </summary>
+    /// <param name="attachments">that are sent with the submission</param>
+    /// <returns>the step where additional data can be added to the submission</returns>
+    public ISenderWithAttachments WithAttachments(params Attachment[] attachments);
+}
diff --git a/FitConnect/Interfaces/Subscriber/ISubscriber.cs b/FitConnect/Interfaces/Subscriber/ISubscriber.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d782ef47c4b05b780eeb52420a295704f3418c5f
--- /dev/null
+++ b/FitConnect/Interfaces/Subscriber/ISubscriber.cs
@@ -0,0 +1,36 @@
+using FitConnect.Models;
+using FitConnect.Services.Models.v1.Submission;
+
+namespace FitConnect.Interfaces.Subscriber;
+
+public interface ISubscriber : IFitConnectClient {
+    /// <summary>
+    ///     Loads a list of available Submissions that were submitted to the subscriber.
+    /// </summary>
+    /// <param name="destinationId">unique identifier of the destination</param>
+    /// <param name="offset">the offset of the list of submissions</param>
+    /// <param name="limit">the limit of the list of submissions</param>
+    /// <returns>List of available submissions for pickup</returns>
+    public IEnumerable<SubmissionForPickupDto> GetAvailableSubmissions(string? destinationId = null,
+        int offset = 0, int limit = 100);
+
+    /// <summary>
+    ///     Loads a single Submission by id.
+    /// </summary>
+    /// <param name="submissionId">unique identifier of a <see cref="Submission" /></param>
+    /// <param name="skipSchemaTest"></param>
+    /// <returns>A subscriber object with a submission</returns>
+    public ISubscriberWithSubmission RequestSubmission(string? submissionId,
+        bool skipSchemaTest = false);
+}
+
+public interface IFitConnectClient{
+
+/// <summary>
+    /// Receives the SecurityEventTokens for a given case
+    /// </summary>
+    /// <param name="caseId">ID of the case</param>
+    /// <returns>List of the <see cref="SecurityEventToken"/></returns>
+    public List<SecurityEventToken> GetStatusForSubmission(string caseId);
+
+}
diff --git a/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs b/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs
new file mode 100644
index 0000000000000000000000000000000000000000..dccc20a60a95dae09317486f5e4ce854d7d64258
--- /dev/null
+++ b/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs
@@ -0,0 +1,43 @@
+using FitConnect.Models;
+using FitConnect.Models.v1.Api;
+using Data = FitConnect.Models.Data;
+
+namespace FitConnect.Interfaces.Subscriber;
+
+public interface ISubscriberWithSubmission {
+    public Submission Submission { get; }
+
+    /// <summary>
+    /// Returns the data (Fachdaten) of the submission
+    /// </summary>
+    /// <returns></returns>
+    public string? GetDataJson() => Submission.Data;
+
+    /// <summary>
+    ///     Loads the <see cref="Attachment" />s for the given <see cref="Submission" />.
+    /// </summary>
+    /// <returns>Enumerable of the attachments</returns>
+    public IEnumerable<Attachment> GetAttachments();
+
+    /// <summary>
+    ///     Accept submission and delete it from the server
+    /// </summary>
+    public void AcceptSubmission();
+
+    /// <summary>
+    /// Rejects the submission
+    /// </summary>
+    /// <param name="problems">Reasons for the rejection</param>
+    public void RejectSubmission(params Problems[] problems);
+
+    /// <summary>
+    /// Set submission state to forwarded
+    /// </summary>
+    public void ForwardSubmission();
+
+    /// <summary>
+    /// Set submission state
+    /// </summary>
+    /// <param name="status">state the submission has to be set to</param>
+    public void CompleteSubmission(FinishSubmissionStatus status);
+}
diff --git a/FitConnect/Models/Area.cs b/FitConnect/Models/Area.cs
index 71215cc66b6cdcad6252b9b5a102c0d93d836386..d02e2b530240f66483f1720416af2939cbc79052 100644
--- a/FitConnect/Models/Area.cs
+++ b/FitConnect/Models/Area.cs
@@ -1,3 +1,29 @@
+using System.Text.Json.Serialization;
+
 namespace FitConnect.Models;
 
-public record Area(string Id, string Name, string Type);
+// Root myDeserializedClass = JsonSerializer.Deserialize<Root>(myJsonResponse);
+public class Area {
+    [JsonPropertyName("id")]
+    public string Id { get; set; } = "";
+
+    [JsonPropertyName("name")]
+    public string Name { get; set; } = "";
+
+    [JsonPropertyName("type")]
+    public string Type { get; set; } = "";
+}
+
+public class AreaList {
+    [JsonPropertyName("count")]
+    public int Count { get; set; }
+
+    [JsonPropertyName("offset")]
+    public int Offset { get; set; }
+
+    [JsonPropertyName("totalCount")]
+    public int TotalCount { get; set; }
+
+    [JsonPropertyName("areas")]
+    public List<Area> Areas { get; set; } = new();
+}
diff --git a/FitConnect/Models/Attachment.cs b/FitConnect/Models/Attachment.cs
index 2c1e5da5761e07a7d46931324df81c8fc778d1f0..c33d21d4867987ef6987e69ec08a5ffc90251066 100644
--- a/FitConnect/Models/Attachment.cs
+++ b/FitConnect/Models/Attachment.cs
@@ -1,3 +1,69 @@
+using System.Security.Cryptography;
+using System.Text;
+using FitConnect.Models.Api.Metadata;
+
 namespace FitConnect.Models;
 
-public record Attachment(string Id, byte[] Content, string Hash, string Filename);
+public class Attachment {
+    public Attachment(Api.Metadata.Attachment metadata, byte[] content) {
+        Filename = metadata.Filename;
+        Content = content;
+        MimeType = Path.GetExtension(metadata.Filename) switch {
+            "pdf" => "application/pdf",
+            "xml" => "application/xml",
+            _ => ""
+        };
+    }
+
+    /// <summary>
+    ///     Attachment for a submission.
+    /// </summary>
+    /// <param name="filename">File name to load</param>
+    /// <param name="description">Description of the attachment</param>
+    public Attachment(string filename, string description) {
+        var file = new FileInfo(filename);
+        Filename = filename;
+        Content = File.ReadAllBytes(filename);
+        var extension = file.Extension[1..].ToLower();
+        MimeType = "application/" + extension;
+        Description = description;
+
+
+        Signature = new AttachmentSignature {
+            DetachedSignature = true,
+            SignatureFormat = extension switch {
+                "pdf" => SignatureFormat.Pdf,
+                "xml" => SignatureFormat.Xml,
+                "json" => SignatureFormat.Json,
+                _ => throw new ArgumentOutOfRangeException()
+            }
+        };
+    }
+
+    public string Id { get; } = Guid.NewGuid().ToString();
+
+    public byte[]? Content { get; init; }
+
+    public string? Hash => CalculateHash();
+
+    public string? Filename { get; init; }
+
+    public string? Purpose { get; init; } = "attachment";
+
+    public string MimeType { get; init; }
+
+    public string? Description { get; init; }
+
+    public AttachmentSignature? Signature { get; }
+
+    private string CalculateHash() {
+        return ByteToHexString(SHA512.Create().ComputeHash(Content));
+    }
+
+    private static string ByteToHexString(IEnumerable<byte> data) {
+        var sb = new StringBuilder();
+        foreach (var b in data) sb.Append(b.ToString("x2"));
+
+        return sb.ToString();
+    }
+}
diff --git a/FitConnect/Models/Callback.cs b/FitConnect/Models/Callback.cs
index cd99183d815dd08497737a0749127d1aee6e2a37..b4d7e94fb7bd1cca3711e9ab5ed288d016b870aa 100644
--- a/FitConnect/Models/Callback.cs
+++ b/FitConnect/Models/Callback.cs
@@ -1,6 +1,13 @@
-
+using FitConnect.Services.Models;
 
 namespace FitConnect.Models;
 
 public record Callback(string? Url, string? Secret) {
- }
+    public static explicit operator Callback(CallbackDto dto) {
+        return new Callback(dto?.Url, null);
+    }
+
+    public static explicit operator CallbackDto(Callback model) {
+        return new CallbackDto { Url = model.Url };
+    }
+}
diff --git a/FitConnect/Models/Destination.cs b/FitConnect/Models/Destination.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7230666d2695cb586c0e39d804a357564872e304
--- /dev/null
+++ b/FitConnect/Models/Destination.cs
@@ -0,0 +1,14 @@
+namespace FitConnect.Models;
+
+public class Destination {
+    public string? LeikaKey { get; set; }
+    public string? Ags { get; set; }
+    public string? Ars { get; set; }
+    public string? AreaId { get; set; }
+    public string? DestinationId { get; set; }
+
+    public bool Valid => !((string.IsNullOrWhiteSpace(Ags)
+                            && string.IsNullOrWhiteSpace(Ars)
+                            && string.IsNullOrWhiteSpace(AreaId)) ||
+                           string.IsNullOrWhiteSpace(LeikaKey));
+}
diff --git a/FitConnect/Models/FitConnectEnvironment.cs b/FitConnect/Models/FitConnectEnvironment.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6bd051ba110b9b27058a613703653896f203ad1f
--- /dev/null
+++ b/FitConnect/Models/FitConnectEnvironment.cs
@@ -0,0 +1,74 @@
+namespace FitConnect.Models;
+
+// public enum FitConnectEnvironment {
+//     Development,
+//     Testing,
+//     Production
+// }
+
+public class FitConnectEnvironment {
+    public static readonly FitConnectEnvironment Testing = new(
+        "https://auth-testing.fit-connect.fitko.dev/token",
+        new[] { "https://submission-api-testing.fit-connect.fitko.dev" },
+        "https://routing-api-testing.fit-connect.fitko.dev"
+    );
+
+    public static readonly FitConnectEnvironment Staging = new(
+        "https://auth-refz.fit-connect.fitko.dev/token",
+        new[] { "https://submission-api-refz.fit-connect.fitko.dev" },
+        "https://routing-api-refz.fit-connect.fitko.dev"
+    );
+
+    public static readonly FitConnectEnvironment Production = new(
+        "https://auth.fit-connect.fitko.net/token",
+        new[] { "https://submission-api.fit-connect.fitko.net" },
+        "https://routing-api.fit-connect.fitko.net"
+    );
+
+    public FitConnectEnvironment() {
+    }
+
+    /// <summary>
+    ///     Default constructor.
+    /// </summary>
+    /// <param name="tokenUrl">URL for receiving the OAuth token</param>
+    /// <param name="submissionUrl">URL for the submission API</param>
+    /// <param name="routingUrl">URL for the routing API</param>
+    public FitConnectEnvironment(string tokenUrl, string[] submissionUrl, string routingUrl) {
+        TokenUrl = tokenUrl;
+        SubmissionUrl = submissionUrl;
+        RoutingUrl = routingUrl;
+    }
+
+    /// <summary>
+    ///     URL for receiving the OAuth token.
+    /// </summary>
+    public string TokenUrl { get; }
+
+    /// <summary>
+    ///     URL for the submission API.
+    /// </summary>
+    public string[] SubmissionUrl { get; }
+
+    /// <summary>
+    ///     URL for the routing API.
+    /// </summary>
+    public string RoutingUrl { get; }
+
+    /// <summary>
+    ///     Creates the endpoints for the given environment.
+    /// </summary>
+    /// <param name="fitConnectEnvironment">Environment to get endpoints for</param>
+    /// <returns></returns>
+    /// <exception cref="ArgumentException">Not all environments are ready to use</exception>
+    // public static FitConnectEnvironment Create(FitConnectEnvironment fitConnectEnvironment) {
+    //     return fitConnectEnvironment switch {
+    //         FitConnectEnvironment.Development => DevEnvironment,
+    //         FitConnectEnvironment.Testing => throw new ArgumentException(
+    //             "Not approved for online testing"),
+    //         FitConnectEnvironment.Production => throw new ArgumentException("NOT PRODUCTION READY"),
+    //         _ => throw new ArgumentOutOfRangeException(nameof(fitConnectEnvironment),
+    //             fitConnectEnvironment, null)
+    //     };
+    // }
+}
diff --git a/FitConnect/Models/FitConnectException.cs b/FitConnect/Models/FitConnectException.cs
index fbd8c908b35a8e5fe7bdeb29912b2382866b61ce..91ca6c592ae3525804d42abe2e2e3a576ab3173a 100644
--- a/FitConnect/Models/FitConnectException.cs
+++ b/FitConnect/Models/FitConnectException.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace FitConnect.Models;
 
 /// <summary>
diff --git a/FitConnect/Models/Metadata.cs b/FitConnect/Models/Metadata.cs
index e5268ab7532bf0baedcb5ea7832dd4039884641d..796635939ac23c46adb0a63aca332bec770408be 100644
--- a/FitConnect/Models/Metadata.cs
+++ b/FitConnect/Models/Metadata.cs
@@ -1,9 +1,12 @@
-using System.Security.Cryptography.X509Certificates;
+using Newtonsoft.Json;
 
 namespace FitConnect.Models;
 
-public class Metadata  {
+public class Metadata : Api.Metadata.Metadata {
 }
 
 public class Data {
+    public static Data? FromString(string data) {
+        return JsonConvert.DeserializeObject<Data>(data);
+    }
 }
diff --git a/FitConnect/Models/SecurityEventToken.cs b/FitConnect/Models/SecurityEventToken.cs
index 88a700524c1c8270c9d06685d8e0552cafb0345d..815c9cd344fbc3afb1229270015366b20d266c62 100644
--- a/FitConnect/Models/SecurityEventToken.cs
+++ b/FitConnect/Models/SecurityEventToken.cs
@@ -1,3 +1,82 @@
+using System.Security.Claims;
+using FitConnect.Models.v1.Api;
+using Microsoft.IdentityModel.JsonWebTokens;
+using Newtonsoft.Json;
+
 namespace FitConnect.Models;
 
-public record SecurityEventToken;
+public enum EventType {
+    NotSet,
+    Create,
+    Submit,
+    Notify,
+    Forward,
+    Reject,
+    Accept,
+    Delete
+}
+
+public class SecurityEventToken {
+    public const string CreateSubmissionSchema = "https://schema.fitko.de/fit-connect/events/create-submission";
+    public const string SubmitSubmissionSchema = "https://schema.fitko.de/fit-connect/events/submit-submission";
+    public const string NotifySubmissionSchema = "https://schema.fitko.de/fit-connect/events/notify-submission";
+    public const string ForwardSubmissionSchema = "https://schema.fitko.de/fit-connect/events/forward-submission";
+    public const string RejectSubmissionSchema = "https://schema.fitko.de/fit-connect/events/reject-submission";
+    public const string AcceptSubmissionSchema = "https://schema.fitko.de/fit-connect/events/accept-submission";
+    public const string DeleteSubmissionSchema = "https://schema.fitko.de/fit-connect/events/delete-submission";
+
+    public SecurityEventToken(string jwtEncodedString) {
+        Token = new JsonWebToken(jwtEncodedString);
+        EventType = DecodeEventType(Token.Claims);
+        var iat = Token.Claims.FirstOrDefault(c => c.Type == "iat").Value;
+        if (long.TryParse(iat, out var timeEpoch))
+            EventTime = DateTime.UnixEpoch.AddSeconds(timeEpoch);
+
+        // TODO Fill problems
+    }
+
+    public DateTime EventTime { get; set; }
+    public EventType EventType { get; set; }
+    public List<Problems>? Problems { get; set; }
+    public Events? Event { get; set; }
+
+    public object? Payload { get; set; }
+    public JsonWebToken Token { get; set; }
+
+    private EventType DecodeEventType(IEnumerable<Claim> claims) {
+        var eventsClaim = claims.FirstOrDefault(c => c.Type == "events");
+        if (eventsClaim == null) return EventType.NotSet;
+
+        var events = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(eventsClaim.Value);
+        Payload = JsonConvert
+            .DeserializeObject<dynamic>(
+                events.Values.FirstOrDefault().ToString());
+
+
+        if (eventsClaim.Value.Contains(
+                CreateSubmissionSchema))
+            return EventType.Create;
+
+        if (eventsClaim.Value.Contains(
+                SubmitSubmissionSchema))
+            return EventType.Submit;
+
+        if (eventsClaim.Value.Contains(
+                NotifySubmissionSchema))
+            return EventType.Notify;
+        if (eventsClaim.Value.Contains(
+                ForwardSubmissionSchema))
+            return EventType.Forward;
+        if (eventsClaim.Value.Contains(
+                RejectSubmissionSchema))
+            return EventType.Reject;
+        if (eventsClaim.Value.Contains(
+                AcceptSubmissionSchema))
+            return EventType.Accept;
+        if (eventsClaim.Value.Contains(
+                DeleteSubmissionSchema))
+            return EventType.Delete;
+
+        return EventType.NotSet;
+    }
+}
diff --git a/FitConnect/Models/ServiceType.cs b/FitConnect/Models/ServiceType.cs
index 1de00dd23cfbe49e8c1f9adeebef196b72a394ec..270aac944633df1257cd4889be64e8c5b211b312 100644
--- a/FitConnect/Models/ServiceType.cs
+++ b/FitConnect/Models/ServiceType.cs
@@ -1,4 +1,4 @@
-
+using FitConnect.Services.Models;
 
 namespace FitConnect.Models;
 
@@ -8,6 +8,21 @@ public class ServiceType {
     public string? Description { get; set; }
     public string? Identifier { get; set; }
 
+    public static explicit operator ServiceType(ServiceTypeDto dto) {
+        return new ServiceType {
+            Description = dto.Description,
+            Identifier = dto.Identifier,
+            Name = dto.Name
+        };
+    }
+
+    public static explicit operator ServiceTypeDto(ServiceType model) {
+        return new ServiceTypeDto {
+            Description = model.Description,
+            Identifier = model.Identifier,
+            Name = model.Name
+        };
+    }
 
     public bool IsValid() {
         return !string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Identifier);
diff --git a/FitConnect/Models/Submission.cs b/FitConnect/Models/Submission.cs
index a6f18fcb3aed374406c1df5a7833a71040b8635d..63bc466e4cabc6fbb950150fd37cf6d2afea16e7 100644
--- a/FitConnect/Models/Submission.cs
+++ b/FitConnect/Models/Submission.cs
@@ -1,26 +1,12 @@
-
-
-using System.Collections.Generic;
+using FitConnect.Services.Models;
+using FitConnect.Services.Models.v1.Submission;
 
 namespace FitConnect.Models;
 
-public class Destination {
-    public string LeikaKey { get; set; }
-    public string? Ags { get; set; }
-    public string? Ars { get; set; }
-    public string? AreaId { get; set; }
-    public string DestinationId { get; set; }
-
-    public bool Valid => !((string.IsNullOrWhiteSpace(Ags)
-                            && string.IsNullOrWhiteSpace(Ars)
-                            && string.IsNullOrWhiteSpace(AreaId)) ||
-                           string.IsNullOrWhiteSpace(LeikaKey));
-}
-
 public class Submission {
-    public string Id { get; set; }
+    public string? Id { get; set; }
     public string? CaseId { get; set; }
-    public Destination Destination { get; set; } = new Destination();
+    public Destination? Destination { get; set; } = new();
 
     public string DestinationId {
         get => Destination.DestinationId;
@@ -28,14 +14,15 @@ public class Submission {
     }
 
     public List<Attachment> Attachments { get; set; } = new();
+    public List<string> AttachmentIds { get; set; } = new();
 
-    public ServiceType ServiceType { get; init; }
+    public ServiceType ServiceType { get; set; } = new();
 
     public Callback? Callback { get; set; }
-    public Metadata? Metadata { get; set; }
-    public Data? Data { get; set; }
-    public string EncryptedMetadata { get; set; }
-    public string EncryptedData { get; set; }
+    public Api.Metadata.Metadata? Metadata { get; set; }
+    public string? Data { get; set; }
+    public string? EncryptedMetadata { get; set; }
+    public string? EncryptedData { get; set; }
 
     public bool IsSubmissionReadyToAdd(out string? error) {
         var innerError = "";
@@ -56,4 +43,52 @@ public class Submission {
         return true;
     }
 
+    public static implicit operator SubmissionForPickupDto(Submission sub) {
+        return new SubmissionForPickupDto {
+            SubmissionId = sub.Id,
+            CaseId = sub.CaseId,
+            DestinationId = sub.DestinationId
+        };
+    }
+
+    public static explicit operator Submission(SubmissionForPickupDto dto) {
+        return new Submission {
+            Id = dto.SubmissionId,
+            Callback = null,
+            DestinationId = dto.DestinationId,
+            ServiceType = null
+        };
+    }
+
+    public static explicit operator Submission(SubmissionDto dto) {
+        return new Submission {
+            Id = dto.SubmissionId,
+            Callback = (Callback)dto.Callback,
+            DestinationId = dto.DestinationId,
+            ServiceType = (ServiceType)dto.ServiceType,
+            EncryptedData = dto.EncryptedData,
+            EncryptedMetadata = dto.EncryptedMetadata,
+            AttachmentIds = dto.Attachments,
+            CaseId = dto.CaseId
+        };
+    }
+
+    public static explicit operator SubmitSubmissionDto(Submission dto) {
+        return new SubmitSubmissionDto {
+            EncryptedData = dto.EncryptedData,
+            EncryptedMetadata = dto.EncryptedMetadata
+        };
+    }
+
+    public static explicit operator CreateSubmissionDto(Submission sub) {
+        var result = new CreateSubmissionDto {
+            AnnouncedAttachments = sub.Attachments.Select(a => a.Id).ToList(),
+            DestinationId = sub.DestinationId
+        };
+        if (sub.ServiceType != null)
+            result.ServiceType = (ServiceTypeDto)sub.ServiceType;
+        if (sub.Callback != null) result.Callback = (CallbackDto)sub.Callback;
+
+        return result;
+    }
 }
diff --git a/FitConnect/Security/IEncryption.cs b/FitConnect/Security/IEncryption.cs
deleted file mode 100644
index 92bf328814bab3acb703c1c0bc28d018a5d6302a..0000000000000000000000000000000000000000
--- a/FitConnect/Security/IEncryption.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.Security.Cryptography.X509Certificates;
-
-namespace FitConnect.Security;
-
-public interface IEncryption {
-    /// <summary>
-    ///     Just for Proof of Concept
-    /// </summary>
-    /// <returns></returns>
-    string GetTestToken();
-
-    void ImportCertificate(X509Certificate2 cert);
-
-    /// <summary>
-    ///     Import a public key from a PEM file
-    /// </summary>
-    /// <param name="certificatePath"></param>
-    /// <param name="password">Password for the certificate</param>
-    /// <exception cref="ArgumentException"></exception>
-    /// <exception cref="Exception"></exception>
-    void ImportCertificate(string certificatePath, string password);
-
-    byte[] DecryptData(byte[] data);
-    string DecryptData(string data);
-    
-    byte[] ExportPublicKey();
-    byte[] ExportPrivateKey();
-    byte[] EncryptData(byte[] data);
-    byte[] EncryptData(byte[] data, byte[] publicKey);
-}
diff --git a/FitConnect/Security/RsaEncryption.cs b/FitConnect/Security/RsaEncryption.cs
deleted file mode 100644
index 570622b28626b12b6e9b7a45e7f817a9265be2cc..0000000000000000000000000000000000000000
--- a/FitConnect/Security/RsaEncryption.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using System;
-using System.IdentityModel.Tokens.Jwt;
-using System.IO;
-using System.Security.Claims;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using Microsoft.Extensions.Logging;
-using Microsoft.IdentityModel.Tokens;
-
-namespace FitConnect.Security;
-
-public class RsaEncryption : IEncryption {
-    private readonly X509Certificate2? _certificate;
-    private readonly ILogger? _logger;
-    private readonly RSA _rsa;
-    private RSA? _privateKey;
-    private RSA? _publicKey;
-
-
-    public RsaEncryption(X509Certificate2? certificate, ILogger? logger) {
-        _logger = logger;
-        _rsa = RSA.Create(4096);
-
-        if (certificate != null) {
-            _certificate = certificate;
-            ImportCertificate(certificate);
-        }
-    }
-
-
-    /// <summary>
-    ///     Just for Proof of Concept
-    /// </summary>
-    /// <returns></returns>
-    public string GetTestToken() {
-        var handler = new JwtSecurityTokenHandler();
-        var token = new SecurityTokenDescriptor {
-            Issuer = "FitConnect",
-            Audience = "FitConnect",
-            EncryptingCredentials =
-                new X509EncryptingCredentials(_certificate ??
-                                              new X509Certificate2(CreateSelfSignedCertificate())),
-            Subject = new ClaimsIdentity(new Claim[] {
-                new("Content", "Unencrypted content")
-            })
-        };
-        return handler.CreateEncodedJwt(token);
-    }
-
-
-    public void ImportCertificate(X509Certificate2 cert) {
-        if (!CheckCertificate(cert)) throw new Exception("Invalid certificate");
-
-        _publicKey = cert.GetRSAPublicKey();
-
-        if ((_publicKey?.KeySize ?? 0) == 0)
-            throw new Exception("Invalid certificate, no public key");
-
-        if (cert.HasPrivateKey) _privateKey = cert.GetRSAPrivateKey();
-
-        if (_privateKey != null)
-            _logger?.LogInformation("Certificate with private key imported");
-        else
-            _logger?.LogInformation("Certificate has no private key");
-    }
-
-    /// <summary>
-    ///     Import a public key from a PEM file
-    /// </summary>
-    /// <param name="certificatePath"></param>
-    /// <param name="password">Password for the certificate</param>
-    /// <exception cref="ArgumentException"></exception>
-    /// <exception cref="Exception"></exception>
-    public void ImportCertificate(string certificatePath, string password) {
-        _logger?.LogInformation("Importing certificate {CertPath}", certificatePath);
-        var cert =
-            new X509Certificate2(certificatePath, password, X509KeyStorageFlags.MachineKeySet);
-
-        if (!CheckCertificate(cert)) throw new ArgumentException("Certificate is not valid");
-
-        var parameters = cert.PublicKey.GetRSAPublicKey()?.ExportParameters(true);
-        if (parameters == null) throw new Exception("Could not get public key from certificate");
-
-        _rsa.ImportParameters(parameters.Value);
-    }
-
-    public byte[] DecryptData(byte[] data) {
-        return (_privateKey ?? _rsa).Decrypt(data, RSAEncryptionPadding.OaepSHA256);
-    }
-
-    public string DecryptData(string data) {
-        return Encoding.UTF8.GetString(DecryptData(Convert.FromBase64String(data)));
-    }
-
-    public byte[] ExportPublicKey() {
-        return _rsa.ExportRSAPublicKey();
-    }
-
-    public byte[] ExportPrivateKey() {
-        return _rsa.ExportRSAPrivateKey();
-    }
-
-    public byte[] EncryptData(byte[] data) {
-        return (_publicKey ?? _rsa).Encrypt(data, RSAEncryptionPadding.OaepSHA256);
-    }
-
-    public byte[] EncryptData(byte[] data, byte[] publicKey) {
-        _logger?.LogInformation(
-            "Encrypting data with public key: {}",
-            Convert.ToBase64String(_rsa.ExportRSAPublicKey()));
-
-        _rsa.ImportRSAPublicKey(publicKey, out var read);
-        return _rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
-    }
-
-    /// <summary>
-    ///     Creating a self signed certificate
-    /// </summary>
-    /// <param name="exportPath">Location for storing the certificate files</param>
-    /// <returns></returns>
-    /// <exception cref="Exception"></exception>
-    public static X509Certificate2 CreateSelfSignedCertificate(string? exportPath = null) {
-        var rsa = RSA.Create(4096);
-
-        var req = new CertificateRequest("c=DE, cn=fitconnect.de",
-            rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
-
-        var cert = req.CreateSelfSigned(DateTimeOffset.Now.AddSeconds(-5),
-            DateTimeOffset.Now.AddYears(5));
-
-        if (cert.GetRSAPublicKey() == null)
-            throw new Exception("Certificate does not contain a public key");
-
-        if (cert.GetRSAPrivateKey() == null)
-            throw new Exception("Certificate does not contain a private key");
-
-        // Export the certificate to a PEM file, just for
-        // additional external testing
-        if (exportPath != null) ExportCertificateToFile(exportPath, cert);
-
-        return cert;
-    }
-
-    private static void ExportCertificateToFile(string exportPath, X509Certificate cert) {
-        // Create PFX (PKCS #12) with private key
-        File.WriteAllBytes($"{exportPath}/certificate.pfx",
-            cert.Export(X509ContentType.Pfx, ""));
-
-        // Create Base 64 encoded CER (public key only)
-        File.WriteAllText($"{exportPath}/certificate.cer",
-            "-----BEGIN CERTIFICATE-----\r\n"
-            + Convert.ToBase64String(cert.Export(X509ContentType.Cert, ""),
-                Base64FormattingOptions.InsertLineBreaks)
-            + "\r\n-----END CERTIFICATE-----");
-    }
-
-
-    /// <summary>
-    ///     Checking the certificate for validity
-    /// </summary>
-    /// <param name="cert">Certificate to check</param>
-    /// <returns>true for a fully approved certificate</returns>
-    private bool CheckCertificate(X509Certificate2 cert) {
-        return true;
-    }
-}
diff --git a/FitConnect/Sender.cs b/FitConnect/Sender.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f56fe72f6b981dcceda3dbaa364321c85b64086b
--- /dev/null
+++ b/FitConnect/Sender.cs
@@ -0,0 +1,247 @@
+using System.Text.RegularExpressions;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Interfaces.Sender;
+using FitConnect.Models;
+using FitConnect.Models.Api.Metadata;
+using FitConnect.Services.Models.v1.Submission;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Attachment = FitConnect.Models.Attachment;
+using Data = FitConnect.Models.Api.Metadata.Data;
+using Metadata = FitConnect.Models.Metadata;
+
+namespace FitConnect;
+
+/// <summary>
+///     The fluent implementation for the <see cref="Sender" /> to encapsulate the FitConnect API.
+/// </summary>
+/// <example>
+///     Reference for FluentSender
+///     <code>
+/// client.Sender
+///     .Authenticate(clientId!, clientSecret!)
+///     .CreateSubmission(new Submission { Attachments = new List() })
+///     .UploadAttachments()
+///     .WithData(data)
+///     .Subm@it();
+/// </code>
+/// </example>
+public class Sender : FitConnectClient, ISender, ISenderWithDestination,
+    ISenderWithAttachments, ISenderWithData, ISenderWithService {
+    public Sender(FitConnectEnvironment environment, string clientId, string clientSecret,
+        ILogger? logger = null) : base(environment, clientId, clientSecret, logger) {
+    }
+
+    public Sender(FitConnectEnvironment environment, string clientId, string clientSecret,
+        IContainer container) : base(environment, clientId, clientSecret,
+        container) {
+    }
+
+    public string? PublicKey { get; set; }
+
+
+    public ISenderWithDestination FindDestinationId(string leiaKey, string? ags = null,
+        string? ars = null,
+        string? areaId = null) {
+        if (ags == null && ars == null && areaId == null)
+            throw new ArgumentException("One of the following must be provided: ags, ars, areaId");
+
+        var destinationId = RouteService.GetDestinationIdAsync(leiaKey, ags, ars, areaId).Result;
+        Logger?.LogInformation("Received destinations: {Destinations}",
+            destinationId.Select(d => d.DestinationId).Aggregate((a, b) => a + "," + b));
+        return WithDestination(destinationId.First().DestinationId);
+    }
+
+    public ISenderWithDestination WithDestination(string destinationId) {
+        if (!Regex.IsMatch(destinationId,
+                "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"))
+            throw new ArgumentException("The destination must be a valid GUID");
+
+        Submission = CreateSubmission(destinationId).Result;
+        return this;
+    }
+
+    public ISenderWithData WithData(string data) {
+        try {
+            JsonConvert.DeserializeObject(data);
+        }
+        catch (Exception e) {
+            throw new ArgumentException("The data must be valid JSON string", e);
+        }
+
+
+        Submission!.Data = data;
+        return this;
+    }
+    
+    public Submission? Submission { get; set; }
+
+    Submission ISenderReady.Submit() {
+        if (Submission == null) {
+            Logger?.LogCritical("Submission is null on submit");
+            throw new InvalidOperationException("Submission is not ready");
+        }
+
+        var metadata = CreateMetadata(Submission);
+        Logger?.LogTrace("MetaData: {metadata}", metadata);
+        Logger?.LogInformation("Sending submission");
+        var encryptedMeta = Encryption.Encrypt(metadata);
+        Logger?.LogTrace("Encrypted metadata: {encryptedMeta}", encryptedMeta);
+        Submission.EncryptedMetadata = encryptedMeta;
+        if (Submission.Data != null)
+            Submission.EncryptedData = Encryption.Encrypt(Submission.Data);
+
+        var result = SubmissionService
+            .SubmitSubmission(Submission.Id!, (SubmitSubmissionDto)Submission).Result;
+
+        Logger?.LogInformation("Submission sent");
+        return Submission;
+    }
+
+
+    public ISenderWithService WithServiceType(string serviceName, string leikaKey) {
+        if (string.IsNullOrWhiteSpace(leikaKey) || !Regex.IsMatch(leikaKey,
+                "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$")) {
+            Logger?.LogError("The leikaKey must be a valid URN");
+            throw new ArgumentException("Invalid leika key");
+        }
+
+        Submission!.ServiceType = new ServiceType {
+            Name = serviceName,
+            Identifier = leikaKey
+        };
+        return this;
+    }
+
+    /// <summary>
+    /// 
+    /// </summary>
+    /// <param name="attachments"></param>
+    /// <returns></returns>
+    public ISenderWithAttachments WithAttachments(params Attachment[] attachments) =>
+        WithAttachments(attachments.ToList());
+    
+    
+    /// <summary>
+    /// </summary>
+    /// <param name="attachments"></param>
+    /// <returns></returns>
+    /// <exception cref="InvalidOperationException"></exception>
+    /// <exception cref="ArgumentException"></exception>
+    public ISenderWithAttachments WithAttachments(IEnumerable<Attachment> attachments) {
+        Submission!.Attachments = new List<Attachment>();
+        foreach (var attachment in attachments) Submission!.Attachments.Add(attachment);
+
+        if (Submission.ServiceType == null) {
+            Logger?.LogError("Submission has no service type");
+            throw new ArgumentException("Submission has no service type");
+        }
+
+        var created = SubmissionService.CreateSubmission((CreateSubmissionDto)Submission);
+        Submission.Id = created.SubmissionId;
+        Submission.CaseId = created.CaseId;
+
+        Logger?.LogInformation("Submission Id {CreatedSubmissionId}, CaseId {SubmissionCaseId}",
+            created.SubmissionId, Submission.CaseId);
+
+        var encryptedAttachments = Encrypt(PublicKey!, Submission.Attachments);
+        UploadAttachmentsAsync(Submission.Id!, encryptedAttachments).Wait();
+
+        return this;
+    }
+
+    public ISenderWithDestination FindDestinationId(Destination destination) {
+        throw new NotImplementedException();
+    }
+
+    /// <summary>
+    ///     Creates a new <see cref="Submission" /> on the FitConnect server.
+    /// </summary>
+    /// <param name="destinationId">The id of the destination the submission has to be sent to</param>
+    /// <returns></returns>
+    /// <exception cref="InvalidOperationException">If sender is not authenticated</exception>
+    /// <exception cref="ArgumentException">If submission is not ready to be sent</exception>
+    private async Task<Submission> CreateSubmission(string destinationId) {
+        PublicKey = await GetPublicKeyFromDestination(destinationId);
+        Encryption = new FitEncryption(Logger) { PublicKeyEncryption = PublicKey };
+
+        var submission = new Submission {
+            DestinationId = destinationId
+        };
+
+        return submission;
+    }
+
+    private async Task<string> GetPublicKeyFromDestination(string destinationId) {
+        var publicKey = await DestinationService.GetPublicKey(destinationId);
+        return publicKey;
+    }
+
+    /// <summary>
+    ///     Create Metadata incl. Hash
+    /// </summary>
+    /// <param name="submission"></param>
+    /// <returns></returns>
+    public static string CreateMetadata(Submission submission) {
+        var data = new Data {
+            Hash = new DataHash {
+                Type = "sha512",
+                Content = FitEncryption.CalculateHash(submission.Data ?? "")
+            },
+
+            SubmissionSchema = new Fachdatenschema {
+                SchemaUri = submission.ServiceType.Identifier,
+                MimeType = "application/json"
+            }
+        };
+        var contentStructure = new ContentStructure {
+            Data = data,
+            Attachments = submission.Attachments.Select(a =>
+                new Models.Api.Metadata.Attachment {
+                    Description = a.Description,
+                    AttachmentId = a.Id,
+                    MimeType = a.MimeType,
+                    Filename = a.Filename,
+                    Purpose = "attachment",
+                    Hash = new AttachmentHash {
+                        Type = "sha512",
+                        Content = a.Hash
+                    }
+                }).ToList()
+        };
+
+        var metaData = new Metadata {
+            ContentStructure = contentStructure
+        };
+        return JsonConvert.SerializeObject(metaData);
+    }
+
+    public IEnumerable<Area> GetAreas(string filter, out int totalCount, int offset = 0,
+        int limit = 100) {
+        var dto = RouteService.GetAreas(filter, offset, limit).Result;
+        totalCount = dto?.TotalCount ?? 0;
+        return dto?.Areas ?? new List<Area>();
+    }
+
+    /// <summary>
+    ///     Uploading the encrypted data to the server
+    /// </summary>
+    /// <param name="submissionId">Submissions ID</param>
+    /// <param name="encryptedAttachments">Encrypted attachments with id and content</param>
+    private async Task<bool> UploadAttachmentsAsync(string submissionId,
+        Dictionary<string, string> encryptedAttachments) {
+        try {
+            foreach (var (id, content) in encryptedAttachments) {
+                Logger?.LogInformation("Uploading attachment {id}", id);
+                SubmissionService.UploadAttachment(submissionId, id, content);
+            }
+
+            return true;
+        }
+        catch (Exception e) {
+            Logger?.LogError("Error Uploading attachment {message}", e.Message);
+            throw;
+        }
+    }
+}
diff --git a/FitConnect/Services/CasesService.cs b/FitConnect/Services/CasesService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5012c6bde657c1faef9e8f8d3481f8d83761bf11
--- /dev/null
+++ b/FitConnect/Services/CasesService.cs
@@ -0,0 +1,50 @@
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models.v1.Case;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.Services;
+
+public class CasesService : RestCallService, ICasesService {
+    private readonly IOAuthService _oAuthService;
+
+    public CasesService(string baseUrl, IOAuthService oAuthService, string version = "v1",
+        ILogger? logger = null) : base(
+        $"{baseUrl}/{version}", logger) {
+        _oAuthService = oAuthService;
+    }
+
+    public string FinishSubmission(string caseId, string token) {
+        _oAuthService.EnsureAuthenticated();
+        var result = RestCallForString($"/cases/{caseId}/events", HttpMethod.Post, token
+                , "application/jose"
+            )
+            .Result;
+
+        return result;
+    }
+
+
+    /// <summary>
+    ///     <para>
+    ///         @GetMapping("/cases/{caseId}/events")
+    ///     </para>
+    /// </summary>
+    /// <param name="caseId">PathVariable</param>
+    /// <param name="offset">RequestParam</param>
+    /// <param name="limit">RequestParam</param>
+    /// <returns></returns>
+    public EventLogDto GetEventLog(string caseId, int offset, int limit) {
+        throw new NotImplementedException();
+    }
+
+    // 
+    /// <summary>
+    ///     <para>@PostMapping(value = "/cases/{caseId}/events", consumes = "application/jose")</para>
+    /// </summary>
+    /// <param name="caseId">PathVariable</param>
+    /// <param name="eventToken">RequestBody</param>
+    /// <returns></returns>
+    public bool ProcessCaseEvent(string caseId, string eventToken) {
+        throw new NotImplementedException();
+    }
+}
diff --git a/FitConnect/Services/DestinationService.cs b/FitConnect/Services/DestinationService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..dfe7b95357dc910501a82415094da74792b64169
--- /dev/null
+++ b/FitConnect/Services/DestinationService.cs
@@ -0,0 +1,97 @@
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models.v1.Destination;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.Services;
+
+public class DestinationService : RestCallService, IDestinationService {
+    private const string ApiVersion = "v1";
+    private readonly IOAuthService _oAuthService;
+
+    public DestinationService(string baseUrl, IOAuthService oAuthService, string version = "v1",
+        ILogger? logger = null) : base(
+        $"{baseUrl}/{version}", logger) {
+        _oAuthService = oAuthService;
+    }
+
+    /// <summary>
+    ///     <para>@PostMapping("/destinations")</para>
+    /// </summary>
+    /// <param name="createDestination">RequestBody</param>
+    /// <returns></returns>
+        public PrivateDestinationDto CreateDestination(CreateDestinationDto createDestination) {
+        throw new NotImplementedException();
+    }
+
+
+    /// <summary>
+    ///     <para>@GetMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+        public void GetDestination(string destinationId) {
+        throw new NotImplementedException();
+    }
+
+
+    /// <summary>
+    ///     <para>@DeleteMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <returns></returns>
+        public bool DeleteDestination(string destinationId) {
+        throw new NotImplementedException();
+    }
+
+
+    /// <summary>
+    ///     <para>@PatchMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <param name="patchDestination">RequestBody</param>
+    /// <returns></returns>
+        public PrivateDestinationDto PatchDestination(string destinationId,
+        PatchDestinationDto patchDestination) {
+        throw new NotImplementedException();
+    }
+
+
+    /// <summary>
+    ///     <para>@GetMapping("/destinations")</para>
+    /// </summary>
+    /// <param name="offset">RequestParam</param>
+    /// <param name="limit">RequestParam</param>
+    /// <returns></returns>
+        public DestinationListDto ListDestinations(int offset, int limit) {
+        throw new NotImplementedException();
+    }
+
+
+    /// <summary>
+    ///     <para>@PutMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <param name="updateDestination">RequestBody</param>
+    /// <returns></returns>
+        public PrivateDestinationDto UpdateDestination(string destinationId,
+        UpdateDestinationDto updateDestination) {
+        throw new NotImplementedException();
+    }
+
+    public async Task<string> GetPublicKey(string destinationId) {
+        _oAuthService.EnsureAuthenticated();
+        var destinationInfo =
+            await RestCall<PublicDestinationDto>($"/destinations/{destinationId}",
+                HttpMethod.Get);
+
+        if (destinationInfo?.EncryptionKid == null)
+            throw new ArgumentException("EncryptionKid was not received");
+
+        var keyId = destinationInfo.EncryptionKid;
+
+        // /v1/destinations/{{destinationId}}/keys/{{keyId}
+        var result = await RestCallForString($"/destinations/{destinationId}/keys/{keyId}",
+            HttpMethod.Get);
+
+        return result;
+    }
+}
diff --git a/FitConnect/Services/InfoService.cs b/FitConnect/Services/InfoService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..86dcd83a1d752b2e0993d90cb5b39cdbdcc1e3a0
--- /dev/null
+++ b/FitConnect/Services/InfoService.cs
@@ -0,0 +1,14 @@
+namespace FitConnect.Services;
+
+public class InfoService : RestCallService {
+    public InfoService(string baseUrl) : base(baseUrl) {
+    }
+
+    /// <summary>
+    ///     @GetMapping("/info")
+    /// </summary>
+    /// <returns></returns>
+        public string GetInfo() {
+        throw new NotImplementedException();
+    }
+}
diff --git a/FitConnect/Services/Interfaces/IDestinationService.cs b/FitConnect/Services/Interfaces/IDestinationService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..44e370d49eff3601f7a2808e92425abe4189bef6
--- /dev/null
+++ b/FitConnect/Services/Interfaces/IDestinationService.cs
@@ -0,0 +1,58 @@
+using FitConnect.Services.Models.v1.Destination;
+
+namespace FitConnect.Services.Interfaces;
+
+public interface IDestinationService : IRestCallService {
+    /// <summary>
+    ///     <para>@PostMapping("/destinations")</para>
+    /// </summary>
+    /// <param name="createDestination">RequestBody</param>
+    /// <returns></returns>
+    PrivateDestinationDto CreateDestination(CreateDestinationDto createDestination);
+
+    /// <summary>
+    ///     <para>@GetMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    void GetDestination(string destinationId);
+
+    /// <summary>
+    ///     <para>@DeleteMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <returns></returns>
+    bool DeleteDestination(string destinationId);
+
+    /// <summary>
+    ///     <para>@PatchMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <param name="patchDestination">RequestBody</param>
+    /// <returns></returns>
+    PrivateDestinationDto PatchDestination(string destinationId,
+        PatchDestinationDto patchDestination);
+
+    /// <summary>
+    ///     <para>@GetMapping("/destinations")</para>
+    /// </summary>
+    /// <param name="offset">RequestParam</param>
+    /// <param name="limit">RequestParam</param>
+    /// <returns></returns>
+    DestinationListDto ListDestinations(int offset, int limit);
+
+    /// <summary>
+    ///     <para>@PutMapping("/destinations/{destinationId}")</para>
+    /// </summary>
+    /// <param name="destinationId">PathVariable</param>
+    /// <param name="updateDestination">RequestBody</param>
+    /// <returns></returns>
+    PrivateDestinationDto UpdateDestination(string destinationId,
+        UpdateDestinationDto updateDestination);
+
+    /// <summary>
+    ///     <para>@GetMapping("/v1/destinations/{{destinationId}}/keys/{{keyId}")</para>
+    /// </summary>
+    /// <param name="destinationId"></param>
+    /// <returns></returns>
+    Task<string> GetPublicKey(string destinationId);
+}
diff --git a/FitConnect/Services/Interfaces/IOAuthService.cs b/FitConnect/Services/Interfaces/IOAuthService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..30da0edc50089aa4778a391d08ac8d4a283ad128
--- /dev/null
+++ b/FitConnect/Services/Interfaces/IOAuthService.cs
@@ -0,0 +1,23 @@
+namespace FitConnect.Services.Interfaces;
+
+public interface IOAuthService : IRestCallService {
+    bool IsAuthenticated { get; }
+
+
+    /// <summary>
+    ///     Requesting an OAuth token from the FitConnect API.
+    ///     <para>
+    ///         You can get the Client ID and Client Secret from the FitConnect Self Service portal
+    ///         under <br />
+    ///         https://portal.auth-testing.fit-connect.fitko.dev
+    ///     </para>
+    /// </summary>
+    /// <param name="clientId">Your client Id</param>
+    /// <param name="clientSecret">Your client Secret</param>
+    /// <param name="scope">Scope if needed</param>
+    /// <returns>The received token or null</returns>
+    Task AuthenticateAsync(
+        string? scope = null);
+
+    public void EnsureAuthenticated();
+}
diff --git a/FitConnect/Services/Interfaces/IRouteService.cs b/FitConnect/Services/Interfaces/IRouteService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..42e0c535c2a38f80e255168ab37538174ed8b91e
--- /dev/null
+++ b/FitConnect/Services/Interfaces/IRouteService.cs
@@ -0,0 +1,27 @@
+using FitConnect.Models;
+using Route = FitConnect.Services.Models.v1.Routes.Route;
+
+namespace FitConnect.Services.Interfaces;
+
+public interface IRouteService : IRestCallService {
+    /// <summary>
+    ///     Returns the destination id for the given intent.
+    /// </summary>
+    /// <param name="leiaKey"></param>
+    /// <param name="ags"></param>
+    /// <param name="ars"></param>
+    /// <param name="areaId"></param>
+    /// <returns></returns>
+        Task<List<Route>> GetDestinationIdAsync(string leiaKey,
+        string? ags = null,
+        string? ars = null,
+        string? areaId = null);
+
+    /// <summary>
+    /// </summary>
+    /// <param name="filter"></param>
+    /// <param name="offset"></param>
+    /// <param name="limit"></param>
+    /// <returns></returns>
+    Task<AreaList?> GetAreas(string filter, int offset = 0, int limit = 100);
+}
diff --git a/FitConnect/Services/Interfaces/ISubmissionService.cs b/FitConnect/Services/Interfaces/ISubmissionService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..becef9e08764b194886c1031e3c118d0174d3a84
--- /dev/null
+++ b/FitConnect/Services/Interfaces/ISubmissionService.cs
@@ -0,0 +1,91 @@
+using FitConnect.Services.Models.v1.Submission;
+
+namespace FitConnect.Services.Interfaces;
+
+public interface ISubmissionService : IRestCallService {
+    /// <summary>
+    ///     <para>@PostMapping("/submissions")</para>
+    /// </summary>
+    /// <param name="submissionDto">RequestBody</param>
+    /// <returns></returns>
+        SubmissionCreatedDto CreateSubmission(CreateSubmissionDto submissionDto);
+
+    /// <summary>
+    ///     <para>
+    ///         @PutMapping(value = "/submissions/{submissionId}/attachments/{attachmentId}", consumes =
+    ///         "application/jose")
+    ///     </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="attachmentId">PathVariable</param>
+    /// <param name="encryptedAttachmentContent">RequestBody</param>
+    /// <returns></returns>
+        bool AddSubmissionAttachment(string submissionId, string attachmentId,
+        string encryptedAttachmentContent);
+
+    /// <summary>
+    ///     <para>@PutMapping(value = "/submissions/{submissionId}", consumes = "application/json") </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="submitSubmission">RequestBody</param>
+    /// <returns></returns>
+        Task<SubmissionReducedDto?> SubmitSubmission(string submissionId,
+        SubmitSubmissionDto submitSubmission);
+
+    /// <summary>
+    ///     <para>@GetMapping("/submissions")</para>
+    /// </summary>
+    /// <param name="destinationId">RequestParam</param>
+    /// <param name="offset">RequestParam</param>
+    /// <param name="limit">RequestParam</param>
+    /// <returns></returns>
+        Task<SubmissionsForPickupDto> ListSubmissions(string? destinationId, int offset,
+        int limit);
+
+    /// <summary>
+    ///     <para>@GetMapping("/submissions/{submissionId}")</para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <returns></returns>
+        SubmissionDto GetSubmission(string submissionId);
+
+    /// <summary>
+    ///     <para>
+    ///         @GetMapping(value = "/submissions/{submissionId}/attachments/{attachmentId}", produces =
+    ///         "application/jose")
+    ///     </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="attachmentId">PathVariable</param>
+    /// <returns></returns>
+        string GetAttachment(string submissionId, string attachmentId);
+
+    /// <summary>
+    ///     Receiving the encryption key from the submission api
+    /// </summary>
+    /// <param name="keyId"></param>
+    /// <returns></returns>
+    string GetKey(string keyId);
+
+
+    /// <summary>
+    ///     Uploads an attachment to the submission api
+    /// </summary>
+    /// <param name="submissionId"></param>
+    /// <param name="attachmentId"></param>
+    /// <param name="content"></param>
+    void UploadAttachment(string submissionId, string attachmentId, string content) {
+        AddSubmissionAttachment(submissionId, attachmentId, content);
+    }
+
+    /// <summary>
+    ///     Retrieves the events of a submission
+    /// </summary>
+    /// <param name="caseId"></param>
+    /// <returns></returns>
+    Task<List<string>> GetStatusForSubmissionAsync(string caseId);
+}
+
+public interface ICasesService {
+    public string FinishSubmission(string caseId, string token);
+}
diff --git a/FitConnect/Services/Models/ApiInfoDto.cs b/FitConnect/Services/Models/ApiInfoDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5cc8756064c2fa0b71a55d252659ace554ac430e
--- /dev/null
+++ b/FitConnect/Services/Models/ApiInfoDto.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Services.Models;
+
+public class ApiInfoDto {
+}
diff --git a/FitConnect/Services/Models/CallbackDto.cs b/FitConnect/Services/Models/CallbackDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0228e1f89d81fb88a94ded3a2d5f45f55f8789f0
--- /dev/null
+++ b/FitConnect/Services/Models/CallbackDto.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models;
+
+public class CallbackDto {
+    [JsonPropertyName("url")]
+    public string? Url { get; set; }
+
+    [JsonPropertyName("secret")]
+    public string? Secret { get; set; }
+}
diff --git a/FitConnect/Services/Models/OAuthAccessToken.cs b/FitConnect/Services/Models/OAuthAccessToken.cs
new file mode 100644
index 0000000000000000000000000000000000000000..077cb92c3c859057dc72e6d848ad72272a401b70
--- /dev/null
+++ b/FitConnect/Services/Models/OAuthAccessToken.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models;
+
+public class OAuthAccessToken {
+    private readonly DateTime createdAt = DateTime.Now;
+
+    [JsonPropertyName("access_token")]
+    public string? AccessToken { get; set; }
+
+    [JsonPropertyName("scope")]
+    public string? Scope { get; set; }
+
+    [JsonPropertyName("token_type")]
+    public string? TokenType { get; set; }
+
+    [JsonPropertyName("expires_in")]
+    public int ExpiresIn { get; set; }
+
+    public bool IsValid => createdAt.AddSeconds(ExpiresIn) > DateTime.Now;
+}
diff --git a/FitConnect/Services/Models/ServiceTypeDto.cs b/FitConnect/Services/Models/ServiceTypeDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..97b5a604fec11305eb21684cc6675b828f121ddb
--- /dev/null
+++ b/FitConnect/Services/Models/ServiceTypeDto.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models;
+
+public class ServiceTypeDto {
+    [JsonPropertyName("name")]
+    public string? Name { get; set; }
+
+    [JsonPropertyName("description")]
+    public string? Description { get; set; }
+
+    [JsonPropertyName("identifier")]
+    public string? Identifier { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Api/Metadata.cs b/FitConnect/Services/Models/v1/Api/Metadata.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c5ccaf433011c00cc3ca3068b80d95a09ac78c45
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Api/Metadata.cs
@@ -0,0 +1,572 @@
+// <auto-generated />
+//
+// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
+//
+//    using FitConnect;
+//
+//    var metadata = Metadata.FromJson(jsonString);
+
+namespace FitConnect.Models.Api.Metadata
+{
+    using System;
+    using System.Collections.Generic;
+
+    using System.Globalization;
+    using Newtonsoft.Json;
+    using Newtonsoft.Json.Converters;
+
+    public class Metadata
+    {
+        /// <summary>
+        /// Eine Struktur, um zusätzliche Informationen zu hinterlegen
+        /// </summary>
+        [JsonProperty("additionalReferenceInfo", NullValueHandling = NullValueHandling.Ignore)]
+        public AdditionalReferenceInfo AdditionalReferenceInfo { get; set; }
+
+        /// <summary>
+        /// Eine Liste aller Identifikationsnachweise der Einreichung.
+        /// </summary>
+        [JsonProperty("authenticationInformation", NullValueHandling = NullValueHandling.Ignore)]
+        public List<AuthenticationInformation> AuthenticationInformation { get; set; }
+
+        /// <summary>
+        /// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
+        /// Fachdaten.
+        /// </summary>
+        [JsonProperty("contentStructure")]
+        public ContentStructure ContentStructure { get; set; }
+
+        /// <summary>
+        /// Dieses Objekt enthält die Informationen vom Bezahldienst.
+        /// </summary>
+        [JsonProperty("paymentInformation", NullValueHandling = NullValueHandling.Ignore)]
+        public PaymentInformation PaymentInformation { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("publicServiceType", NullValueHandling = NullValueHandling.Ignore)]
+        public Verwaltungsleistung PublicServiceType { get; set; }
+
+        [JsonProperty("replyChannel", NullValueHandling = NullValueHandling.Ignore)]
+        public ReplyChannel ReplyChannel { get; set; }
+    }
+
+    /// <summary>
+    /// Eine Struktur, um zusätzliche Informationen zu hinterlegen
+    /// </summary>
+    public partial class AdditionalReferenceInfo
+    {
+        /// <summary>
+        /// Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der
+        /// Einreichung des Antrags über FIT-Connect sein.
+        /// </summary>
+        [JsonProperty("applicationDate", NullValueHandling = NullValueHandling.Ignore)]
+        public DateTimeOffset? ApplicationDate { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("senderReference", NullValueHandling = NullValueHandling.Ignore)]
+        public string SenderReference { get; set; }
+    }
+
+    /// <summary>
+    /// Eine Struktur, die einen Identifikationsnachweis beschreibt.
+    /// </summary>
+    public partial class AuthenticationInformation
+    {
+        /// <summary>
+        /// Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Definiert die Art des Identifikationsnachweises.
+        /// </summary>
+        [JsonProperty("type")]
+        public AuthenticationInformationType Type { get; set; }
+
+        /// <summary>
+        /// semver kompatible Versionsangabe des genutzten Nachweistyps.
+        /// </summary>
+        [JsonProperty("version")]
+        public string Version { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
+    /// Fachdaten.
+    /// </summary>
+    public partial class ContentStructure
+    {
+        [JsonProperty("attachments")]
+        public List<Attachment> Attachments { get; set; }
+
+        /// <summary>
+        /// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
+        /// </summary>
+        [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
+        public Data Data { get; set; }
+    }
+
+    /// <summary>
+    /// Eine in der Einreichung enthaltene Anlage.
+    /// </summary>
+    public partial class Attachment
+    {
+        /// <summary>
+        /// Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.
+        /// </summary>
+        [JsonProperty("attachmentId")]
+        public string AttachmentId { get; set; }
+
+        /// <summary>
+        /// Optionale Beschreibung der Anlage
+        /// </summary>
+        [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// Ursprünglicher Dateiname bei Erzeugung oder Upload
+        /// </summary>
+        [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
+        public string Filename { get; set; }
+
+        /// <summary>
+        /// 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).
+        /// </summary>
+        [JsonProperty("hash")]
+        public AttachmentHash Hash { get; set; }
+
+        /// <summary>
+        /// Internet Media Type gemäß RFC 2045, z. B. application/pdf.
+        /// </summary>
+        [JsonProperty("mimeType")]
+        public string MimeType { get; set; }
+
+        /// <summary>
+        /// Zweck/Art der Anlage
+        /// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
+        /// - attachment: Anlage, die von einem Bürger hochgeladen wurde
+        /// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
+        /// </summary>
+        [JsonProperty("purpose")]
+        public string Purpose { get; set; }
+
+        [JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
+        public AttachmentSignature Signature { get; set; }
+    }
+
+    /// <summary>
+    /// 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).
+    /// </summary>
+    public partial class AttachmentHash
+    {
+        /// <summary>
+        /// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+        /// </summary>
+        [JsonProperty("type")]
+        public string Type { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt das Signaturformt und Profile
+    /// </summary>
+    public partial class AttachmentSignature
+    {
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("detachedSignature")]
+        public bool DetachedSignature { get; set; }
+
+        /// <summary>
+        /// 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).
+        ///
+        /// Fü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
+        /// </summary>
+        [JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
+        public EidasAdesProfile? EidasAdesProfile { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("signatureFormat")]
+        public SignatureFormat SignatureFormat { get; set; }
+    }
+
+    /// <summary>
+    /// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
+    /// </summary>
+    public partial class Data
+    {
+        /// <summary>
+        /// 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).
+        /// </summary>
+        [JsonProperty("hash")]
+        public DataHash Hash { get; set; }
+
+        /// <summary>
+        /// Beschreibt das Signaturformt und Profile
+        /// </summary>
+        [JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
+        public DataSignature Signature { get; set; }
+
+        /// <summary>
+        /// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
+        /// </summary>
+        [JsonProperty("submissionSchema")]
+        public Fachdatenschema SubmissionSchema { get; set; }
+    }
+
+    /// <summary>
+    /// 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).
+    /// </summary>
+    public partial class DataHash
+    {
+        /// <summary>
+        /// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+        /// </summary>
+        [JsonProperty("type")]
+        public string Type { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt das Signaturformt und Profile
+    /// </summary>
+    public partial class DataSignature
+    {
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("detachedSignature")]
+        public bool DetachedSignature { get; set; }
+
+        /// <summary>
+        /// 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).
+        ///
+        /// Fü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
+        /// </summary>
+        [JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
+        public EidasAdesProfile? EidasAdesProfile { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("signatureFormat")]
+        public SignatureFormat SignatureFormat { get; set; }
+    }
+
+    /// <summary>
+    /// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
+    /// </summary>
+    public partial class Fachdatenschema
+    {
+        /// <summary>
+        /// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
+        /// XSD- oder JSON-Schema).
+        /// </summary>
+        [JsonProperty("mimeType")]
+        public string MimeType { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("schemaUri")]
+        public string SchemaUri { get; set; }
+    }
+
+    /// <summary>
+    /// Dieses Objekt enthält die Informationen vom Bezahldienst.
+    /// </summary>
+    public partial class PaymentInformation
+    {
+        /// <summary>
+        /// Bruttobetrag
+        /// </summary>
+        [JsonProperty("grossAmount", NullValueHandling = NullValueHandling.Ignore)]
+        
+        public double? GrossAmount { get; set; }
+
+        /// <summary>
+        /// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
+        /// vorhanden / befüllt.
+        /// </summary>
+        [JsonProperty("paymentMethod")]
+        public PaymentMethod PaymentMethod { get; set; }
+
+        /// <summary>
+        /// Weitere Erläuterung zur gewählten Zahlart.
+        /// </summary>
+        [JsonProperty("paymentMethodDetail", NullValueHandling = NullValueHandling.Ignore)]
+        
+        public string PaymentMethodDetail { get; set; }
+
+        /// <summary>
+        /// - 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.
+        /// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
+        /// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
+        /// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
+        /// </summary>
+        [JsonProperty("status")]
+        public Status Status { get; set; }
+
+        /// <summary>
+        /// Eine vom Bezahldienst vergebene Transaktions-Id.
+        /// </summary>
+        [JsonProperty("transactionId")]
+        
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen.
+        /// </summary>
+        [JsonProperty("transactionReference")]
+        public string TransactionReference { get; set; }
+
+        /// <summary>
+        /// Zeitstempel der erfolgreichen Durchführung der Bezahlung.
+        /// </summary>
+        [JsonProperty("transactionTimestamp", NullValueHandling = NullValueHandling.Ignore)]
+        public DateTimeOffset? TransactionTimestamp { get; set; }
+
+        /// <summary>
+        /// Die Rest-URL der Payment Transaction für die Statusabfrage.
+        /// </summary>
+        [JsonProperty("transactionUrl", NullValueHandling = NullValueHandling.Ignore)]
+        public Uri TransactionUrl { get; set; }
+    }
+
+    /// <summary>
+    /// 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.
+    /// </summary>
+    public partial class Verwaltungsleistung
+    {
+        /// <summary>
+        /// (Kurz-)Beschreibung der Verwaltungsleistung
+        /// </summary>
+        [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier
+        /// `urn:de:fim:leika:leistung:` vorangestellt werden.
+        /// </summary>
+        [JsonProperty("identifier")]
+        
+        public string Identifier { get; set; }
+
+        /// <summary>
+        /// Name/Bezeichnung der Verwaltungsleistung
+        /// </summary>
+        [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+        public string Name { get; set; }
+    }
+
+    public partial class ReplyChannel
+    {
+        /// <summary>
+        /// Akkreditierte Anbieter siehe
+        /// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
+        /// </summary>
+        [JsonProperty("deMail", NullValueHandling = NullValueHandling.Ignore)]
+        public DeMail DeMail { get; set; }
+
+        /// <summary>
+        /// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
+        /// </summary>
+        [JsonProperty("elster", NullValueHandling = NullValueHandling.Ignore)]
+        public Elster Elster { get; set; }
+
+        [JsonProperty("eMail", NullValueHandling = NullValueHandling.Ignore)]
+        public EMail EMail { get; set; }
+
+        /// <summary>
+        /// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
+        /// </summary>
+        [JsonProperty("fink", NullValueHandling = NullValueHandling.Ignore)]
+        public Fink Fink { get; set; }
+    }
+
+    /// <summary>
+    /// Akkreditierte Anbieter siehe
+    /// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
+    /// </summary>
+    public partial class DeMail
+    {
+        [JsonProperty("address")]
+        public string Address { get; set; }
+    }
+
+    public partial class EMail
+    {
+        [JsonProperty("address")]
+        public string Address { get; set; }
+
+        /// <summary>
+        /// Hilfe zur Erstellung gibt es in der Dokumentation unter
+        /// https://docs.fitko.de/fit-connect/details/pgp-export
+        /// </summary>
+        [JsonProperty("pgpPublicKey", NullValueHandling = NullValueHandling.Ignore)]
+        public string PgpPublicKey { get; set; }
+    }
+
+    /// <summary>
+    /// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
+    /// </summary>
+    public partial class Elster
+    {
+        [JsonProperty("accountId")]
+        public string AccountId { get; set; }
+
+        [JsonProperty("geschaeftszeichen", NullValueHandling = NullValueHandling.Ignore)]
+        
+        public string Geschaeftszeichen { get; set; }
+
+        [JsonProperty("lieferTicket", NullValueHandling = NullValueHandling.Ignore)]
+        public string LieferTicket { get; set; }
+    }
+
+    /// <summary>
+    /// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
+    /// </summary>
+    public partial class Fink
+    {
+        /// <summary>
+        /// FINK Postfachadresse
+        /// </summary>
+        [JsonProperty("finkPostfachRef")]
+        
+        public string FinkPostfachRef { get; set; }
+
+        /// <summary>
+        /// URL des Servicekontos, in dem das Ziel-Postfach liegt
+        /// </summary>
+        [JsonProperty("host", NullValueHandling = NullValueHandling.Ignore)]
+        public Uri Host { get; set; }
+    }
+
+    /// <summary>
+    /// Definiert die Art des Identifikationsnachweises.
+    /// </summary>
+    public enum AuthenticationInformationType { IdentificationReport };
+
+    /// <summary>
+    /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+    /// </summary>
+    public enum HashType { Sha512 };
+
+    /// <summary>
+    /// Zweck/Art der Anlage
+    /// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
+    /// - attachment: Anlage, die von einem Bürger hochgeladen wurde
+    /// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
+    /// </summary>
+    public enum Purpose { Attachment, Form, Report };
+
+    /// <summary>
+    /// 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).
+    ///
+    /// Fü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
+    /// </summary>
+    public enum EidasAdesProfile { HttpUriEtsiOrgAdes191X2LevelBaselineBB, HttpUriEtsiOrgAdes191X2LevelBaselineBLt, HttpUriEtsiOrgAdes191X2LevelBaselineBLta, HttpUriEtsiOrgAdes191X2LevelBaselineBT };
+
+    /// <summary>
+    /// 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.
+    /// </summary>
+    public enum SignatureFormat { Asic, Cms, Json, Pdf, Xml };
+
+    /// <summary>
+    /// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
+    /// XSD- oder JSON-Schema).
+    /// </summary>
+    //public enum MimeType { ApplicationJson, ApplicationXml };
+
+    /// <summary>
+    /// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
+    /// vorhanden / befüllt.
+    /// </summary>
+    public enum PaymentMethod { Creditcard, Giropay, Invoice, Other, Paydirect, Paypal };
+
+    /// <summary>
+    /// - 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.
+    /// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
+    /// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
+    /// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
+    /// </summary>
+    public enum Status { Booked, Canceled, Failed, Initial };
+    
+}
diff --git a/FitConnect/Services/Models/v1/Api/SecEventToken.cs b/FitConnect/Services/Models/v1/Api/SecEventToken.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a6d73d83b95a49eab53be6f110bdb4145d6966f9
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Api/SecEventToken.cs
@@ -0,0 +1,415 @@
+// <auto-generated />
+//
+// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
+//
+//    using FitConnect.Models.v1.Api;
+//
+//    var welcome = Welcome.FromJson(jsonString);
+
+namespace FitConnect.Models.v1.Api
+{
+    using System;
+    using System.Collections.Generic;
+
+    using System.Globalization;
+    using Newtonsoft.Json;
+    using Newtonsoft.Json.Converters;
+
+    public partial class SecurityEventToken
+    {
+        [JsonProperty("$schema")]
+        public Uri Schema { get; set; }
+
+        [JsonProperty("$id")]
+        public Uri Id { get; set; }
+
+        [JsonProperty("title")]
+        public string Title { get; set; }
+
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public WelcomeProperties Properties { get; set; }
+
+        [JsonProperty("additionalProperties")]
+        public bool AdditionalProperties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] WelcomeRequired { get; set; }
+    }
+
+    public partial class WelcomeProperties
+    {
+        [JsonProperty("$schema")]
+        public Schema Schema { get; set; }
+
+        [JsonProperty("jti")]
+        public Jti Jti { get; set; }
+
+        [JsonProperty("iss")]
+        public Iss Iss { get; set; }
+
+        [JsonProperty("iat")]
+        public Iat Iat { get; set; }
+
+        [JsonProperty("sub")]
+        public Sub Sub { get; set; }
+
+        [JsonProperty("txn")]
+        public Sub Txn { get; set; }
+
+        [JsonProperty("events")]
+        public Events Events { get; set; }
+    }
+
+    public partial class Events
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public EventsProperties Properties { get; set; }
+
+        [JsonProperty("additionalProperties")]
+        public bool AdditionalProperties { get; set; }
+
+        [JsonProperty("minProperties")]
+        public long MinProperties { get; set; }
+
+        [JsonProperty("maxProperties")]
+        public long MaxProperties { get; set; }
+    }
+
+    public partial class EventsProperties
+    {
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/create-submission")]
+        public Iss HttpsSchemaFitkoDeFitConnectEventsCreateSubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/submit-submission")]
+        public HttpsSchemaFitkoDeFitConnectEventsSubmitSubmission HttpsSchemaFitkoDeFitConnectEventsSubmitSubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/notify-submission")]
+        public HttpsSchemaFitkoDeFitConnectEventsNotifySubmission HttpsSchemaFitkoDeFitConnectEventsNotifySubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/forward-submission")]
+        public Iss HttpsSchemaFitkoDeFitConnectEventsForwardSubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/reject-submission")]
+        public HttpsSchemaFitkoDeFitConnectEventsRejectSubmission HttpsSchemaFitkoDeFitConnectEventsRejectSubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/accept-submission")]
+        public HttpsSchemaFitkoDeFitConnectEventsAcceptSubmission HttpsSchemaFitkoDeFitConnectEventsAcceptSubmission { get; set; }
+
+        [JsonProperty("https://schema.fitko.de/fit-connect/events/delete-submission")]
+        public Iss HttpsSchemaFitkoDeFitConnectEventsDeleteSubmission { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsAcceptSubmission
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public HttpsSchemaFitkoDeFitConnectEventsAcceptSubmissionProperties Properties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] HttpsSchemaFitkoDeFitConnectEventsAcceptSubmissionRequired { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsAcceptSubmissionProperties
+    {
+        [JsonProperty("problems")]
+        public AuthenticationTags Problems { get; set; }
+
+        [JsonProperty("authenticationTags")]
+        public AuthenticationTags AuthenticationTags { get; set; }
+    }
+
+    public partial class AuthenticationTags
+    {
+        [JsonProperty("$ref")]
+        public string Ref { get; set; }
+    }
+
+    public partial class Iss
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsNotifySubmission
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public HttpsSchemaFitkoDeFitConnectEventsNotifySubmissionProperties Properties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] HttpsSchemaFitkoDeFitConnectEventsNotifySubmissionRequired { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsNotifySubmissionProperties
+    {
+        [JsonProperty("notifyType")]
+        public NotifyType NotifyType { get; set; }
+    }
+
+    public partial class NotifyType
+    {
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("enum")]
+        public string[] Enum { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsRejectSubmission
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public HttpsSchemaFitkoDeFitConnectEventsRejectSubmissionProperties Properties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] HttpsSchemaFitkoDeFitConnectEventsRejectSubmissionRequired { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsRejectSubmissionProperties
+    {
+        [JsonProperty("problems")]
+        public Problems Problems { get; set; }
+    }
+
+    public partial class Problems {
+        public static readonly Problems IncorrectAuthenticationTag = new Problems();
+        public static readonly Problems EncryptionIssue = new Problems();
+        public static readonly Problems SyntaxViolation = new Problems();
+        public static readonly Problems MissingSchema = new Problems();
+        public static readonly Problems UnsupportedSchema = new Problems();
+        public static readonly Problems SchemaViolation = new Problems();
+        
+        [JsonProperty("title")]
+        public string Title { get; set; }
+
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("items")]
+        public Items Items { get; set; }
+    }
+
+    public partial class Items
+    {
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public ItemsProperties Properties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] ItemsRequired { get; set; }
+    }
+
+    public partial class ItemsProperties
+    {
+        [JsonProperty("type")]
+        public Sub Type { get; set; }
+
+        [JsonProperty("title")]
+        public Iss Title { get; set; }
+
+        [JsonProperty("detail")]
+        public Iss Detail { get; set; }
+
+        [JsonProperty("instance")]
+        public Sub Instance { get; set; }
+    }
+
+    public partial class Sub
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("pattern")]
+        public string Pattern { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsSubmitSubmission
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public HttpsSchemaFitkoDeFitConnectEventsSubmitSubmissionProperties Properties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] HttpsSchemaFitkoDeFitConnectEventsSubmitSubmissionRequired { get; set; }
+    }
+
+    public partial class HttpsSchemaFitkoDeFitConnectEventsSubmitSubmissionProperties
+    {
+        [JsonProperty("authenticationTags")]
+        public PurpleAuthenticationTags AuthenticationTags { get; set; }
+    }
+
+    public partial class PurpleAuthenticationTags
+    {
+        [JsonProperty("title")]
+        public string Title { get; set; }
+
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("properties")]
+        public AuthenticationTagsProperties Properties { get; set; }
+
+        [JsonProperty("additionalProperties")]
+        public bool AdditionalProperties { get; set; }
+
+        [JsonProperty("required")]
+        public string[] AuthenticationTagsRequired { get; set; }
+    }
+
+    public partial class AuthenticationTagsProperties
+    {
+        [JsonProperty("metadata")]
+        public AuthenticationTags Metadata { get; set; }
+
+        [JsonProperty("data")]
+        public Data Data { get; set; }
+
+        [JsonProperty("attachments")]
+        public Attachments Attachments { get; set; }
+    }
+
+    public partial class Attachments
+    {
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("patternProperties")]
+        public PatternProperties PatternProperties { get; set; }
+
+        [JsonProperty("additionalProperties")]
+        public bool AdditionalProperties { get; set; }
+    }
+
+    public partial class PatternProperties
+    {
+        [JsonProperty("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")]
+        public AuthenticationTags The09AFAF809AFAF409AFAF409AFAF409AFAF12 { get; set; }
+    }
+
+    public partial class Data
+    {
+        [JsonProperty("title")]
+        public string Title { get; set; }
+
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("pattern")]
+        public string Pattern { get; set; }
+
+        [JsonProperty("minLength")]
+        public long MinLength { get; set; }
+
+        [JsonProperty("maxLength")]
+        public long MaxLength { get; set; }
+    }
+
+    public partial class Iat
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("minimum")]
+        public long Minimum { get; set; }
+    }
+
+    public partial class Jti
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("format")]
+        public string Format { get; set; }
+    }
+
+    public partial class Schema
+    {
+        [JsonProperty("description")]
+        public string Description { get; set; }
+
+        [JsonProperty("type")]
+        public string Type { get; set; }
+
+        [JsonProperty("enum")]
+        public Uri[] Enum { get; set; }
+    }
+
+    public partial class SecurityEventToken
+    {
+        public static SecurityEventToken FromJson(string json) => JsonConvert.DeserializeObject<SecurityEventToken>(json, FitConnect.Models.v1.Api.Converter.Settings);
+    }
+
+    public static class Serialize
+    {
+        public static string ToJson(this SecurityEventToken self) => JsonConvert.SerializeObject(self, FitConnect.Models.v1.Api.Converter.Settings);
+    }
+
+    internal static class Converter
+    {
+        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
+        {
+            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
+            DateParseHandling = DateParseHandling.None,
+            Converters =
+            {
+                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
+            },
+        };
+    }
+}
diff --git a/FitConnect/Services/Models/v1/Api/SecurityEventToken.cs b/FitConnect/Services/Models/v1/Api/SecurityEventToken.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9fcbaeb948e6f4ab6f64ae61511502dcdd89e6e8
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Api/SecurityEventToken.cs
@@ -0,0 +1,1122 @@
+// <auto-generated />
+//
+// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
+//
+//    using FitConnect;
+//
+//    var metadata = Metadata.FromJson(jsonString);
+
+namespace FitConnect.Models.Api.Set
+{
+    using System;
+    using System.Collections.Generic;
+
+    using System.Globalization;
+    using Newtonsoft.Json;
+    using Newtonsoft.Json.Converters;
+
+    public partial class Metadata
+    {
+        /// <summary>
+        /// Eine Struktur, um zusätzliche Informationen zu hinterlegen
+        /// </summary>
+        [JsonProperty("additionalReferenceInfo", NullValueHandling = NullValueHandling.Ignore)]
+        public AdditionalReferenceInfo AdditionalReferenceInfo { get; set; }
+
+        /// <summary>
+        /// Eine Liste aller Identifikationsnachweise der Einreichung.
+        /// </summary>
+        [JsonProperty("authenticationInformation", NullValueHandling = NullValueHandling.Ignore)]
+        public List<AuthenticationInformation> AuthenticationInformation { get; set; }
+
+        /// <summary>
+        /// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
+        /// Fachdaten.
+        /// </summary>
+        [JsonProperty("contentStructure")]
+        public ContentStructure ContentStructure { get; set; }
+
+        /// <summary>
+        /// Dieses Objekt enthält die Informationen vom Bezahldienst.
+        /// </summary>
+        [JsonProperty("paymentInformation", NullValueHandling = NullValueHandling.Ignore)]
+        public PaymentInformation PaymentInformation { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("publicServiceType", NullValueHandling = NullValueHandling.Ignore)]
+        public Verwaltungsleistung PublicServiceType { get; set; }
+
+        [JsonProperty("replyChannel", NullValueHandling = NullValueHandling.Ignore)]
+        public ReplyChannel ReplyChannel { get; set; }
+    }
+
+    /// <summary>
+    /// Eine Struktur, um zusätzliche Informationen zu hinterlegen
+    /// </summary>
+    public partial class AdditionalReferenceInfo
+    {
+        /// <summary>
+        /// Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der
+        /// Einreichung des Antrags über FIT-Connect sein.
+        /// </summary>
+        [JsonProperty("applicationDate", NullValueHandling = NullValueHandling.Ignore)]
+        public DateTimeOffset? ApplicationDate { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("senderReference", NullValueHandling = NullValueHandling.Ignore)]
+        public string SenderReference { get; set; }
+    }
+
+    /// <summary>
+    /// Eine Struktur, die einen Identifikationsnachweis beschreibt.
+    /// </summary>
+    public partial class AuthenticationInformation
+    {
+        /// <summary>
+        /// Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Definiert die Art des Identifikationsnachweises.
+        /// </summary>
+        [JsonProperty("type")]
+        public AuthenticationInformationType Type { get; set; }
+
+        /// <summary>
+        /// semver kompatible Versionsangabe des genutzten Nachweistyps.
+        /// </summary>
+        [JsonProperty("version")]
+        public string Version { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
+    /// Fachdaten.
+    /// </summary>
+    public partial class ContentStructure
+    {
+        [JsonProperty("attachments")]
+        public List<Attachment> Attachments { get; set; }
+
+        /// <summary>
+        /// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
+        /// </summary>
+        [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
+        public Data Data { get; set; }
+    }
+
+    /// <summary>
+    /// Eine in der Einreichung enthaltene Anlage.
+    /// </summary>
+    public partial class Attachment
+    {
+        /// <summary>
+        /// Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.
+        /// </summary>
+        [JsonProperty("attachmentId")]
+        public Guid AttachmentId { get; set; }
+
+        /// <summary>
+        /// Optionale Beschreibung der Anlage
+        /// </summary>
+        [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// Ursprünglicher Dateiname bei Erzeugung oder Upload
+        /// </summary>
+        [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
+        public string Filename { get; set; }
+
+        /// <summary>
+        /// 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).
+        /// </summary>
+        [JsonProperty("hash")]
+        public AttachmentHash Hash { get; set; }
+
+        /// <summary>
+        /// Internet Media Type gemäß RFC 2045, z. B. application/pdf.
+        /// </summary>
+        [JsonProperty("mimeType")]
+        public string MimeType { get; set; }
+
+        /// <summary>
+        /// Zweck/Art der Anlage
+        /// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
+        /// - attachment: Anlage, die von einem Bürger hochgeladen wurde
+        /// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
+        /// </summary>
+        [JsonProperty("purpose")]
+        public Purpose Purpose { get; set; }
+
+        [JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
+        public AttachmentSignature Signature { get; set; }
+    }
+
+    /// <summary>
+    /// 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).
+    /// </summary>
+    public partial class AttachmentHash
+    {
+        /// <summary>
+        /// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+        /// </summary>
+        [JsonProperty("type")]
+        public HashType Type { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt das Signaturformt und Profile
+    /// </summary>
+    public partial class AttachmentSignature
+    {
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("detachedSignature")]
+        public bool DetachedSignature { get; set; }
+
+        /// <summary>
+        /// 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).
+        ///
+        /// Fü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
+        /// </summary>
+        [JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
+        public EidasAdesProfile? EidasAdesProfile { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("signatureFormat")]
+        public SignatureFormat SignatureFormat { get; set; }
+    }
+
+    /// <summary>
+    /// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
+    /// </summary>
+    public partial class Data
+    {
+        /// <summary>
+        /// 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).
+        /// </summary>
+        [JsonProperty("hash")]
+        public DataHash Hash { get; set; }
+
+        /// <summary>
+        /// Beschreibt das Signaturformt und Profile
+        /// </summary>
+        [JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
+        public DataSignature Signature { get; set; }
+
+        /// <summary>
+        /// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
+        /// </summary>
+        [JsonProperty("submissionSchema")]
+        public Fachdatenschema SubmissionSchema { get; set; }
+    }
+
+    /// <summary>
+    /// 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).
+    /// </summary>
+    public partial class DataHash
+    {
+        /// <summary>
+        /// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
+        /// </summary>
+        [JsonProperty("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+        /// </summary>
+        [JsonProperty("type")]
+        public HashType Type { get; set; }
+    }
+
+    /// <summary>
+    /// Beschreibt das Signaturformt und Profile
+    /// </summary>
+    public partial class DataSignature
+    {
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("detachedSignature")]
+        public bool DetachedSignature { get; set; }
+
+        /// <summary>
+        /// 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).
+        ///
+        /// Fü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
+        /// </summary>
+        [JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
+        public EidasAdesProfile? EidasAdesProfile { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("signatureFormat")]
+        public SignatureFormat SignatureFormat { get; set; }
+    }
+
+    /// <summary>
+    /// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
+    /// </summary>
+    public partial class Fachdatenschema
+    {
+        /// <summary>
+        /// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
+        /// XSD- oder JSON-Schema).
+        /// </summary>
+        [JsonProperty("mimeType")]
+        public string MimeType { get; set; }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        [JsonProperty("schemaUri")]
+        public Uri SchemaUri { get; set; }
+    }
+
+    /// <summary>
+    /// Dieses Objekt enthält die Informationen vom Bezahldienst.
+    /// </summary>
+    public partial class PaymentInformation
+    {
+        /// <summary>
+        /// Bruttobetrag
+        /// </summary>
+        [JsonProperty("grossAmount", NullValueHandling = NullValueHandling.Ignore)]
+        [JsonConverter(typeof(MinMaxValueCheckConverter))]
+        public double? GrossAmount { get; set; }
+
+        /// <summary>
+        /// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
+        /// vorhanden / befüllt.
+        /// </summary>
+        [JsonProperty("paymentMethod")]
+        public PaymentMethod PaymentMethod { get; set; }
+
+        /// <summary>
+        /// Weitere Erläuterung zur gewählten Zahlart.
+        /// </summary>
+        [JsonProperty("paymentMethodDetail", NullValueHandling = NullValueHandling.Ignore)]
+        [JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
+        public string PaymentMethodDetail { get; set; }
+
+        /// <summary>
+        /// - 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.
+        /// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
+        /// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
+        /// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
+        /// </summary>
+        [JsonProperty("status")]
+        public Status Status { get; set; }
+
+        /// <summary>
+        /// Eine vom Bezahldienst vergebene Transaktions-Id.
+        /// </summary>
+        [JsonProperty("transactionId")]
+        [JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen.
+        /// </summary>
+        [JsonProperty("transactionReference")]
+        public string TransactionReference { get; set; }
+
+        /// <summary>
+        /// Zeitstempel der erfolgreichen Durchführung der Bezahlung.
+        /// </summary>
+        [JsonProperty("transactionTimestamp", NullValueHandling = NullValueHandling.Ignore)]
+        public DateTimeOffset? TransactionTimestamp { get; set; }
+
+        /// <summary>
+        /// Die Rest-URL der Payment Transaction für die Statusabfrage.
+        /// </summary>
+        [JsonProperty("transactionUrl", NullValueHandling = NullValueHandling.Ignore)]
+        public Uri TransactionUrl { get; set; }
+    }
+
+    /// <summary>
+    /// 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.
+    /// </summary>
+    public partial class Verwaltungsleistung
+    {
+        /// <summary>
+        /// (Kurz-)Beschreibung der Verwaltungsleistung
+        /// </summary>
+        [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier
+        /// `urn:de:fim:leika:leistung:` vorangestellt werden.
+        /// </summary>
+        [JsonProperty("identifier")]
+        [JsonConverter(typeof(FluffyMinMaxLengthCheckConverter))]
+        public string Identifier { get; set; }
+
+        /// <summary>
+        /// Name/Bezeichnung der Verwaltungsleistung
+        /// </summary>
+        [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+        public string Name { get; set; }
+    }
+
+    public partial class ReplyChannel
+    {
+        /// <summary>
+        /// Akkreditierte Anbieter siehe
+        /// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
+        /// </summary>
+        [JsonProperty("deMail", NullValueHandling = NullValueHandling.Ignore)]
+        public DeMail DeMail { get; set; }
+
+        /// <summary>
+        /// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
+        /// </summary>
+        [JsonProperty("elster", NullValueHandling = NullValueHandling.Ignore)]
+        public Elster Elster { get; set; }
+
+        [JsonProperty("eMail", NullValueHandling = NullValueHandling.Ignore)]
+        public EMail EMail { get; set; }
+
+        /// <summary>
+        /// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
+        /// </summary>
+        [JsonProperty("fink", NullValueHandling = NullValueHandling.Ignore)]
+        public Fink Fink { get; set; }
+    }
+
+    /// <summary>
+    /// Akkreditierte Anbieter siehe
+    /// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
+    /// </summary>
+    public partial class DeMail
+    {
+        [JsonProperty("address")]
+        public string Address { get; set; }
+    }
+
+    public partial class EMail
+    {
+        [JsonProperty("address")]
+        public string Address { get; set; }
+
+        /// <summary>
+        /// Hilfe zur Erstellung gibt es in der Dokumentation unter
+        /// https://docs.fitko.de/fit-connect/details/pgp-export
+        /// </summary>
+        [JsonProperty("pgpPublicKey", NullValueHandling = NullValueHandling.Ignore)]
+        public string PgpPublicKey { get; set; }
+    }
+
+    /// <summary>
+    /// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
+    /// </summary>
+    public partial class Elster
+    {
+        [JsonProperty("accountId")]
+        public string AccountId { get; set; }
+
+        [JsonProperty("geschaeftszeichen", NullValueHandling = NullValueHandling.Ignore)]
+        [JsonConverter(typeof(TentacledMinMaxLengthCheckConverter))]
+        public string Geschaeftszeichen { get; set; }
+
+        [JsonProperty("lieferTicket", NullValueHandling = NullValueHandling.Ignore)]
+        public string LieferTicket { get; set; }
+    }
+
+    /// <summary>
+    /// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
+    /// </summary>
+    public partial class Fink
+    {
+        /// <summary>
+        /// FINK Postfachadresse
+        /// </summary>
+        [JsonProperty("finkPostfachRef")]
+        [JsonConverter(typeof(StickyMinMaxLengthCheckConverter))]
+        public string FinkPostfachRef { get; set; }
+
+        /// <summary>
+        /// URL des Servicekontos, in dem das Ziel-Postfach liegt
+        /// </summary>
+        [JsonProperty("host", NullValueHandling = NullValueHandling.Ignore)]
+        public Uri Host { get; set; }
+    }
+
+    /// <summary>
+    /// Definiert die Art des Identifikationsnachweises.
+    /// </summary>
+    public enum AuthenticationInformationType { IdentificationReport };
+
+    /// <summary>
+    /// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
+    /// </summary>
+    public enum HashType { Sha512 };
+
+    /// <summary>
+    /// Zweck/Art der Anlage
+    /// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
+    /// - attachment: Anlage, die von einem Bürger hochgeladen wurde
+    /// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
+    /// </summary>
+    public enum Purpose { Attachment, Form, Report };
+
+    /// <summary>
+    /// 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).
+    ///
+    /// Fü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
+    /// </summary>
+    public enum EidasAdesProfile { HttpUriEtsiOrgAdes191X2LevelBaselineBB, HttpUriEtsiOrgAdes191X2LevelBaselineBLt, HttpUriEtsiOrgAdes191X2LevelBaselineBLta, HttpUriEtsiOrgAdes191X2LevelBaselineBT };
+
+    /// <summary>
+    /// 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.
+    /// </summary>
+    public enum SignatureFormat { Asic, Cms, Json, Pdf, Xml };
+
+    /// <summary>
+    /// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
+    /// XSD- oder JSON-Schema).
+    /// </summary>
+    public enum MimeType { ApplicationJson, ApplicationXml };
+
+    /// <summary>
+    /// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
+    /// vorhanden / befüllt.
+    /// </summary>
+    public enum PaymentMethod { Creditcard, Giropay, Invoice, Other, Paydirect, Paypal };
+
+    /// <summary>
+    /// - 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.
+    /// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
+    /// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
+    /// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
+    /// </summary>
+    public enum Status { Booked, Canceled, Failed, Initial };
+
+    public partial class Metadata
+    {
+        public static Metadata FromJson(string json) => JsonConvert.DeserializeObject<Metadata>(json, FitConnect.Models.Api.Set.Converter.Settings);
+    }
+
+    public static class Serialize
+    {
+        public static string ToJson(this Metadata self) => JsonConvert.SerializeObject(self, FitConnect.Models.Api.Set.Converter.Settings);
+    }
+
+    internal static class Converter
+    {
+        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
+        {
+            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
+            DateParseHandling = DateParseHandling.None,
+            Converters =
+            {
+                AuthenticationInformationTypeConverter.Singleton,
+                HashTypeConverter.Singleton,
+                PurposeConverter.Singleton,
+                EidasAdesProfileConverter.Singleton,
+                SignatureFormatConverter.Singleton,
+                MimeTypeConverter.Singleton,
+                PaymentMethodConverter.Singleton,
+                StatusConverter.Singleton,
+                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
+            },
+        };
+    }
+
+    internal class AuthenticationInformationTypeConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(AuthenticationInformationType) || t == typeof(AuthenticationInformationType?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            if (value == "identificationReport")
+            {
+                return AuthenticationInformationType.IdentificationReport;
+            }
+            throw new Exception("Cannot unmarshal type AuthenticationInformationType");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (AuthenticationInformationType)untypedValue;
+            if (value == AuthenticationInformationType.IdentificationReport)
+            {
+                serializer.Serialize(writer, "identificationReport");
+                return;
+            }
+            throw new Exception("Cannot marshal type AuthenticationInformationType");
+        }
+
+        public static readonly AuthenticationInformationTypeConverter Singleton = new AuthenticationInformationTypeConverter();
+    }
+
+    internal class HashTypeConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(HashType) || t == typeof(HashType?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            if (value == "sha512")
+            {
+                return HashType.Sha512;
+            }
+            throw new Exception("Cannot unmarshal type HashType");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (HashType)untypedValue;
+            if (value == HashType.Sha512)
+            {
+                serializer.Serialize(writer, "sha512");
+                return;
+            }
+            throw new Exception("Cannot marshal type HashType");
+        }
+
+        public static readonly HashTypeConverter Singleton = new HashTypeConverter();
+    }
+
+    internal class PurposeConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(Purpose) || t == typeof(Purpose?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "attachment":
+                    return Purpose.Attachment;
+                case "form":
+                    return Purpose.Form;
+                case "report":
+                    return Purpose.Report;
+            }
+            throw new Exception("Cannot unmarshal type Purpose");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (Purpose)untypedValue;
+            switch (value)
+            {
+                case Purpose.Attachment:
+                    serializer.Serialize(writer, "attachment");
+                    return;
+                case Purpose.Form:
+                    serializer.Serialize(writer, "form");
+                    return;
+                case Purpose.Report:
+                    serializer.Serialize(writer, "report");
+                    return;
+            }
+            throw new Exception("Cannot marshal type Purpose");
+        }
+
+        public static readonly PurposeConverter Singleton = new PurposeConverter();
+    }
+
+    internal class EidasAdesProfileConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(EidasAdesProfile) || t == typeof(EidasAdesProfile?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "http://uri.etsi.org/ades/191x2/level/baseline/B-B#":
+                    return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB;
+                case "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#":
+                    return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt;
+                case "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#":
+                    return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta;
+                case "http://uri.etsi.org/ades/191x2/level/baseline/B-T#":
+                    return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT;
+            }
+            throw new Exception("Cannot unmarshal type EidasAdesProfile");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (EidasAdesProfile)untypedValue;
+            switch (value)
+            {
+                case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB:
+                    serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-B#");
+                    return;
+                case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt:
+                    serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#");
+                    return;
+                case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta:
+                    serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#");
+                    return;
+                case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT:
+                    serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-T#");
+                    return;
+            }
+            throw new Exception("Cannot marshal type EidasAdesProfile");
+        }
+
+        public static readonly EidasAdesProfileConverter Singleton = new EidasAdesProfileConverter();
+    }
+
+    internal class SignatureFormatConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(SignatureFormat) || t == typeof(SignatureFormat?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "asic":
+                    return SignatureFormat.Asic;
+                case "cms":
+                    return SignatureFormat.Cms;
+                case "json":
+                    return SignatureFormat.Json;
+                case "pdf":
+                    return SignatureFormat.Pdf;
+                case "xml":
+                    return SignatureFormat.Xml;
+            }
+            throw new Exception("Cannot unmarshal type SignatureFormat");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (SignatureFormat)untypedValue;
+            switch (value)
+            {
+                case SignatureFormat.Asic:
+                    serializer.Serialize(writer, "asic");
+                    return;
+                case SignatureFormat.Cms:
+                    serializer.Serialize(writer, "cms");
+                    return;
+                case SignatureFormat.Json:
+                    serializer.Serialize(writer, "json");
+                    return;
+                case SignatureFormat.Pdf:
+                    serializer.Serialize(writer, "pdf");
+                    return;
+                case SignatureFormat.Xml:
+                    serializer.Serialize(writer, "xml");
+                    return;
+            }
+            throw new Exception("Cannot marshal type SignatureFormat");
+        }
+
+        public static readonly SignatureFormatConverter Singleton = new SignatureFormatConverter();
+    }
+
+    internal class MimeTypeConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(MimeType) || t == typeof(MimeType?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "application/json":
+                    return MimeType.ApplicationJson;
+                case "application/xml":
+                    return MimeType.ApplicationXml;
+            }
+            throw new Exception("Cannot unmarshal type MimeType");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (MimeType)untypedValue;
+            switch (value)
+            {
+                case MimeType.ApplicationJson:
+                    serializer.Serialize(writer, "application/json");
+                    return;
+                case MimeType.ApplicationXml:
+                    serializer.Serialize(writer, "application/xml");
+                    return;
+            }
+            throw new Exception("Cannot marshal type MimeType");
+        }
+
+        public static readonly MimeTypeConverter Singleton = new MimeTypeConverter();
+    }
+
+    internal class MinMaxValueCheckConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(double) || t == typeof(double?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<double>(reader);
+            if (value >= 0.01)
+            {
+                return value;
+            }
+            throw new Exception("Cannot unmarshal type double");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (double)untypedValue;
+            if (value >= 0.01)
+            {
+                serializer.Serialize(writer, value);
+                return;
+            }
+            throw new Exception("Cannot marshal type double");
+        }
+
+        public static readonly MinMaxValueCheckConverter Singleton = new MinMaxValueCheckConverter();
+    }
+
+    internal class PaymentMethodConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(PaymentMethod) || t == typeof(PaymentMethod?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "CREDITCARD":
+                    return PaymentMethod.Creditcard;
+                case "GIROPAY":
+                    return PaymentMethod.Giropay;
+                case "INVOICE":
+                    return PaymentMethod.Invoice;
+                case "OTHER":
+                    return PaymentMethod.Other;
+                case "PAYDIRECT":
+                    return PaymentMethod.Paydirect;
+                case "PAYPAL":
+                    return PaymentMethod.Paypal;
+            }
+            throw new Exception("Cannot unmarshal type PaymentMethod");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (PaymentMethod)untypedValue;
+            switch (value)
+            {
+                case PaymentMethod.Creditcard:
+                    serializer.Serialize(writer, "CREDITCARD");
+                    return;
+                case PaymentMethod.Giropay:
+                    serializer.Serialize(writer, "GIROPAY");
+                    return;
+                case PaymentMethod.Invoice:
+                    serializer.Serialize(writer, "INVOICE");
+                    return;
+                case PaymentMethod.Other:
+                    serializer.Serialize(writer, "OTHER");
+                    return;
+                case PaymentMethod.Paydirect:
+                    serializer.Serialize(writer, "PAYDIRECT");
+                    return;
+                case PaymentMethod.Paypal:
+                    serializer.Serialize(writer, "PAYPAL");
+                    return;
+            }
+            throw new Exception("Cannot marshal type PaymentMethod");
+        }
+
+        public static readonly PaymentMethodConverter Singleton = new PaymentMethodConverter();
+    }
+
+    internal class PurpleMinMaxLengthCheckConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(string);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            var value = serializer.Deserialize<string>(reader);
+            if (value.Length >= 1 && value.Length <= 36)
+            {
+                return value;
+            }
+            throw new Exception("Cannot unmarshal type string");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            var value = (string)untypedValue;
+            if (value.Length >= 1 && value.Length <= 36)
+            {
+                serializer.Serialize(writer, value);
+                return;
+            }
+            throw new Exception("Cannot marshal type string");
+        }
+
+        public static readonly PurpleMinMaxLengthCheckConverter Singleton = new PurpleMinMaxLengthCheckConverter();
+    }
+
+    internal class StatusConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(Status) || t == typeof(Status?);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            if (reader.TokenType == JsonToken.Null) return null;
+            var value = serializer.Deserialize<string>(reader);
+            switch (value)
+            {
+                case "BOOKED":
+                    return Status.Booked;
+                case "CANCELED":
+                    return Status.Canceled;
+                case "FAILED":
+                    return Status.Failed;
+                case "INITIAL":
+                    return Status.Initial;
+            }
+            throw new Exception("Cannot unmarshal type Status");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            if (untypedValue == null)
+            {
+                serializer.Serialize(writer, null);
+                return;
+            }
+            var value = (Status)untypedValue;
+            switch (value)
+            {
+                case Status.Booked:
+                    serializer.Serialize(writer, "BOOKED");
+                    return;
+                case Status.Canceled:
+                    serializer.Serialize(writer, "CANCELED");
+                    return;
+                case Status.Failed:
+                    serializer.Serialize(writer, "FAILED");
+                    return;
+                case Status.Initial:
+                    serializer.Serialize(writer, "INITIAL");
+                    return;
+            }
+            throw new Exception("Cannot marshal type Status");
+        }
+
+        public static readonly StatusConverter Singleton = new StatusConverter();
+    }
+
+    internal class FluffyMinMaxLengthCheckConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(string);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            var value = serializer.Deserialize<string>(reader);
+            if (value.Length >= 7 && value.Length <= 255)
+            {
+                return value;
+            }
+            throw new Exception("Cannot unmarshal type string");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            var value = (string)untypedValue;
+            if (value.Length >= 7 && value.Length <= 255)
+            {
+                serializer.Serialize(writer, value);
+                return;
+            }
+            throw new Exception("Cannot marshal type string");
+        }
+
+        public static readonly FluffyMinMaxLengthCheckConverter Singleton = new FluffyMinMaxLengthCheckConverter();
+    }
+
+    internal class TentacledMinMaxLengthCheckConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(string);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            var value = serializer.Deserialize<string>(reader);
+            if (value.Length <= 10)
+            {
+                return value;
+            }
+            throw new Exception("Cannot unmarshal type string");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            var value = (string)untypedValue;
+            if (value.Length <= 10)
+            {
+                serializer.Serialize(writer, value);
+                return;
+            }
+            throw new Exception("Cannot marshal type string");
+        }
+
+        public static readonly TentacledMinMaxLengthCheckConverter Singleton = new TentacledMinMaxLengthCheckConverter();
+    }
+
+    internal class StickyMinMaxLengthCheckConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(string);
+
+        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+        {
+            var value = serializer.Deserialize<string>(reader);
+            if (value.Length <= 150)
+            {
+                return value;
+            }
+            throw new Exception("Cannot unmarshal type string");
+        }
+
+        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+        {
+            var value = (string)untypedValue;
+            if (value.Length <= 150)
+            {
+                serializer.Serialize(writer, value);
+                return;
+            }
+            throw new Exception("Cannot marshal type string");
+        }
+
+        public static readonly StickyMinMaxLengthCheckConverter Singleton = new StickyMinMaxLengthCheckConverter();
+    }
+}
diff --git a/FitConnect/Services/Models/v1/Case/EventLogDto.cs b/FitConnect/Services/Models/v1/Case/EventLogDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df39168a2c5f9566476a35c78e095409418e3d34
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Case/EventLogDto.cs
@@ -0,0 +1,17 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Case;
+
+public class EventLogDto {
+    [JsonPropertyName("count")]
+    public int Count { get; set; }
+
+    [JsonPropertyName("eventLog")]
+    public List<string>? EventLog { get; set; }
+
+    [JsonPropertyName("offset")]
+    public int Offset { get; set; }
+
+    [JsonPropertyName("totalCount")]
+    public long TotalCount { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/ContactInformationDto.cs b/FitConnect/Services/Models/v1/Destination/ContactInformationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f017503cf6981a04ec603c6a30b4ed6c0d92d017
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/ContactInformationDto.cs
@@ -0,0 +1,20 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class ContactInformationDto {
+    [JsonPropertyName("address")]
+    public string? Address { get; set; }
+
+    [JsonPropertyName("email")]
+    public string? Email { get; set; }
+
+    [JsonPropertyName("legalName")]
+    public string? LegalName { get; set; }
+
+    [JsonPropertyName("phone")]
+    public string? Phone { get; set; }
+
+    [JsonPropertyName("unit")]
+    public string? Unit { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/CreateDestinationDto.cs b/FitConnect/Services/Models/v1/Destination/CreateDestinationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a4fce8a96ccc7363d4c8da5c3925dde9bdfffda3
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/CreateDestinationDto.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class CreateDestinationDto {
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+    [JsonPropertyName("contactInformation")]
+    public ContactInformationDto? ContactInformation { get; set; }
+
+
+    [JsonPropertyName("encryptionKid")]
+    public string? EncryptionKid { get; set; }
+
+
+    [JsonPropertyName("encryptionPublicKey")]
+    public string? EncryptionPublicKey { get; set; }
+
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string>? MetadataVersions { get; set; }
+
+
+    [JsonPropertyName("replyChannels")]
+    public DestinationReplyChannelsDto? ReplyChannels { get; set; }
+
+
+    [JsonPropertyName("services")]
+    public List<DestinationServiceDto>? Services { get; set; }
+
+
+    [JsonPropertyName("signingPublicKey")]
+    public string? SigningPublicKey { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/DeMailDto.cs b/FitConnect/Services/Models/v1/Destination/DeMailDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8990552d438fba85a5e529703404718fc0886044
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/DeMailDto.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class DeMailDto {
+}
diff --git a/FitConnect/Services/Models/v1/Destination/DestinationListDto.cs b/FitConnect/Services/Models/v1/Destination/DestinationListDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9acf39e097ce0e50c90b4b70930068ddacc06710
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/DestinationListDto.cs
@@ -0,0 +1,20 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class DestinationListDto {
+    [JsonPropertyName("count")]
+    public int Count { get; set; }
+
+
+    [JsonPropertyName("destinations")]
+    public List<PrivateDestinationDto>? Destinations { get; set; }
+
+
+    [JsonPropertyName("offset")]
+    public int Offset { get; set; }
+
+
+    [JsonPropertyName("totalCount")]
+    public long TotalCount { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/DestinationReplyChannelsDto.cs b/FitConnect/Services/Models/v1/Destination/DestinationReplyChannelsDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b869115be7c55a1b957c475cd7452149561209d4
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/DestinationReplyChannelsDto.cs
@@ -0,0 +1,28 @@
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class DestinationReplyChannelsDto {
+    public DestinationReplyChannelsDto(
+        EmailDto eMail,
+        DeMailDto deMail,
+        FinkDto fink,
+        ElsterDto elster) {
+        EMail = EMail;
+        DeMail = DeMail;
+        Fink = Fink;
+        Elster = Elster;
+    }
+
+    public DeMailDto? DeMail { get; set; }
+
+
+    public ElsterDto? Elster { get; set; }
+    public EmailDto? EMail { get; set; }
+
+
+    public FinkDto? Fink { get; set; }
+
+
+    public bool IsEmpty() {
+        return EMail == null && DeMail == null && Fink == null && Elster == null;
+    }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/DestinationServiceDto.cs b/FitConnect/Services/Models/v1/Destination/DestinationServiceDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cc100b7fbfe68cede5635dac205a4b25243436bb
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/DestinationServiceDto.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class DestinationServiceDto {
+    [JsonPropertyName("identifier")]
+    public string? Identifier { get; set; }
+
+
+    [JsonPropertyName("regions")]
+    public List<string>? Regions { get; set; }
+
+    [JsonPropertyName("submissionSchemas")]
+    public List<SubmissionSchemaDto>? SubmissionSchemas { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/ElsterDto.cs b/FitConnect/Services/Models/v1/Destination/ElsterDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fbad21ebb00fdfe0c151a9b0d294e3af886075b8
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/ElsterDto.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class ElsterDto {
+}
diff --git a/FitConnect/Services/Models/v1/Destination/EmailDto.cs b/FitConnect/Services/Models/v1/Destination/EmailDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..019230e9f8435dbfb2bad88cdef0e56f7ed7238b
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/EmailDto.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class EmailDto {
+}
diff --git a/FitConnect/Services/Models/v1/Destination/FinkDto.cs b/FitConnect/Services/Models/v1/Destination/FinkDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ff8c80fc72bfff4b67ffff34d4a8936b6a06c387
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/FinkDto.cs
@@ -0,0 +1,4 @@
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class FinkDto {
+}
diff --git a/FitConnect/Services/Models/v1/Destination/PatchDestinationDto.cs b/FitConnect/Services/Models/v1/Destination/PatchDestinationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8ae416957d56358e743dc3cb8f82fd8b686c677a
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/PatchDestinationDto.cs
@@ -0,0 +1,31 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class PatchDestinationDto {
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("contactInformation")]
+    public ContactInformationDto? ContactInformation { get; set; }
+
+
+    [JsonPropertyName("encryptionKid")]
+    public string? EncryptionKid { get; set; }
+
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string>? metadataVersions { get; set; }
+
+
+    [JsonPropertyName("replyChannels")]
+    public DestinationReplyChannelsDto? ReplyChannels { get; set; }
+
+
+    [JsonPropertyName("services")]
+    public List<DestinationServiceDto>? Services { get; set; }
+
+    [JsonPropertyName("status")]
+    public string? Status { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/PrivateDestinationDto.cs b/FitConnect/Services/Models/v1/Destination/PrivateDestinationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b6d9098af49e0de7597a6b667dc7827e7b3e39ae
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/PrivateDestinationDto.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class PrivateDestinationDto {
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("contactInformation")]
+    public ContactInformationDto? ContactInformation { get; set; }
+
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+
+
+    [JsonPropertyName("encryptionKid")]
+    public string? EncryptionKid { get; set; }
+
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string>? MetadataVersions { get; set; }
+
+
+    [JsonPropertyName("replyChannels")]
+    public DestinationReplyChannelsDto? ReplyChannels { get; set; }
+
+
+    [JsonPropertyName("services")]
+    public List<DestinationServiceDto>? Services { get; set; }
+
+
+    [JsonPropertyName("status")]
+    public string? Status { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/PublicDestinationDto.cs b/FitConnect/Services/Models/v1/Destination/PublicDestinationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b188491e560a2bd5b3e9b7d071773e5cfb33cc02
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/PublicDestinationDto.cs
@@ -0,0 +1,29 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+// ReSharper disable once ClassNeverInstantiated.Global
+public class PublicDestinationDto {
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+
+
+    [JsonPropertyName("encryptionKid")]
+    public string? EncryptionKid { get; set; }
+
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string>? MetadataVersions { get; set; }
+
+
+    [JsonPropertyName("replyChannels")]
+    public DestinationReplyChannelsDto? ReplyChannels { get; set; }
+
+
+    [JsonPropertyName("services")]
+    public List<DestinationServiceDto>? Services { get; set; }
+
+
+    [JsonPropertyName("status")]
+    public string? Status { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/SubmissionSchemaDto.cs b/FitConnect/Services/Models/v1/Destination/SubmissionSchemaDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5beadb55742385a881f1c09d3120c097636e32bd
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/SubmissionSchemaDto.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class SubmissionSchemaDto {
+    [JsonPropertyName("mimeType")]
+    // private SubmissionSchemaMimeTypeDto mimeType;
+    public string mimeType;
+
+    [JsonPropertyName("schemaUri")]
+    public string? SchemaUri { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Destination/UpdateDestinationDto.cs b/FitConnect/Services/Models/v1/Destination/UpdateDestinationDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..18a7bd98092524ffc2c6725d394dfcdcce103fe0
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Destination/UpdateDestinationDto.cs
@@ -0,0 +1,31 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Destination;
+
+public class UpdateDestinationDto {
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("contactInformation")]
+    public ContactInformationDto? ContactInformation { get; set; }
+
+
+    [JsonPropertyName("encryptionKid")]
+    public string? EncryptionKid { get; set; }
+
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string>? metadataVersions { get; set; }
+
+
+    [JsonPropertyName("replyChannels")]
+    public DestinationReplyChannelsDto? ReplyChannels { get; set; }
+
+
+    [JsonPropertyName("services")]
+    public List<DestinationServiceDto>? Services { get; set; }
+
+    [JsonPropertyName("status")]
+    public string? Status { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Routes/Routes.cs b/FitConnect/Services/Models/v1/Routes/Routes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..286dbccfa43d8d03238c8c96fb9ef8c5ac1bd8bf
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Routes/Routes.cs
@@ -0,0 +1,110 @@
+// Root myDeserializedClass = JsonSerializer.Deserialize<Root>(myJsonResponse);
+
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+
+namespace FitConnect.Services.Models.v1.Routes;
+
+public class DestinationParameters {
+    [JsonPropertyName("encryptionKid")]
+    public string EncryptionKid { get; set; }
+
+    [JsonPropertyName("metadataVersions")]
+    public List<string> MetadataVersions { get; set; }
+
+    [JsonPropertyName("publicKeys")]
+    public PublicKeys PublicKeys { get; set; }
+
+    [JsonPropertyName("replyChannels")]
+    public ReplyChannels ReplyChannels { get; set; }
+
+    [JsonPropertyName("status")]
+    public string Status { get; set; }
+
+    [JsonPropertyName("submissionSchemas")]
+    public List<SubmissionSchema> SubmissionSchemas { get; set; }
+
+    [JsonPropertyName("submissionUrl")]
+    public string SubmissionUrl { get; set; }
+}
+
+public class EMail {
+    [JsonPropertyName("usePgp")]
+    public bool UsePgp { get; set; }
+}
+
+public class Key {
+    [JsonPropertyName("kty")]
+    public string Kty { get; set; }
+
+    [JsonPropertyName("key_ops")]
+    public List<string> KeyOps { get; set; }
+
+    [JsonPropertyName("alg")]
+    public string Alg { get; set; }
+
+    [JsonPropertyName("x5c")]
+    public List<string> X5c { get; set; }
+
+    [JsonPropertyName("kid")]
+    public string Kid { get; set; }
+
+    [JsonPropertyName("n")]
+    public string N { get; set; }
+
+    [JsonPropertyName("e")]
+    public string E { get; set; }
+
+    public string ToString() {
+        return JsonConvert.SerializeObject(this);
+    }
+}
+
+public class PublicKeys {
+    [JsonPropertyName("keys")]
+    public List<Key> Keys { get; set; }
+}
+
+public class ReplyChannels {
+    [JsonPropertyName("eMail")]
+    public EMail EMail { get; set; }
+}
+
+public class RoutesListDto {
+    [JsonPropertyName("count")]
+    public int Count { get; set; }
+
+    [JsonPropertyName("offset")]
+    public int Offset { get; set; }
+
+    [JsonPropertyName("totalCount")]
+    public int TotalCount { get; set; }
+
+    [JsonPropertyName("routes")]
+    public List<Route> Routes { get; set; }
+}
+
+public class Route {
+    [JsonPropertyName("destinationId")]
+    public string DestinationId { get; set; }
+
+    [JsonPropertyName("destinationSignature")]
+    public string DestinationSignature { get; set; }
+
+    [JsonPropertyName("destinationParameters")]
+    public DestinationParameters DestinationParameters { get; set; }
+
+    [JsonPropertyName("destinationParametersSignature")]
+    public string DestinationParametersSignature { get; set; }
+
+    [JsonPropertyName("destinationName")]
+    public string DestinationName { get; set; }
+}
+
+public class SubmissionSchema {
+    [JsonPropertyName("schemaUri")]
+    public string SchemaUri { get; set; }
+
+    [JsonPropertyName("mimeType")]
+    public string MimeType { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/CreateSubmissionDto.cs b/FitConnect/Services/Models/v1/Submission/CreateSubmissionDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bf340a6f82255a75febc5c8ce53ed6f2dcbafad9
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/CreateSubmissionDto.cs
@@ -0,0 +1,22 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class CreateSubmissionDto {
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("serviceType")]
+    public ServiceTypeDto? ServiceType { get; set; }
+
+
+    [JsonPropertyName("announcedAttachments")]
+    public List<string>? AnnouncedAttachments { get; set; }
+
+    [JsonPropertyName("caseId")]
+    public string? CaseId { get; set; }
+
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmissionCreatedDto.cs b/FitConnect/Services/Models/v1/Submission/SubmissionCreatedDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..307ff526a51be2db21cb5906542e951759def4ff
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmissionCreatedDto.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmissionCreatedDto {
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+
+    [JsonPropertyName("submissionId")]
+    public string? SubmissionId { get; set; }
+
+    [JsonPropertyName("caseId")]
+    public string? CaseId { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmissionDto.cs b/FitConnect/Services/Models/v1/Submission/SubmissionDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fc2203507fe54203974b3096e5ee91241983d8bb
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmissionDto.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmissionDto {
+    [JsonPropertyName("attachments")]
+    public List<string>? Attachments { get; set; }
+
+
+    [JsonPropertyName("callback")]
+    public CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("caseId")]
+    public string? CaseId { get; set; }
+
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+
+
+    [JsonPropertyName("encryptedData")]
+    public string? EncryptedData { get; set; }
+
+
+    [JsonPropertyName("encryptedMetadata")]
+    public string? EncryptedMetadata { get; set; }
+
+
+    [JsonPropertyName("serviceType")]
+    public ServiceTypeDto? ServiceType { get; set; }
+
+
+    [JsonPropertyName("submissionId")]
+    public string? SubmissionId { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmissionForPickupDto.cs b/FitConnect/Services/Models/v1/Submission/SubmissionForPickupDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b01e4ac60bbbf8d2e82b9dff39abacc0ad9d0299
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmissionForPickupDto.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmissionForPickupDto {
+    [JsonPropertyName("caseId")]
+    public string? CaseId { get; set; }
+
+    [JsonPropertyName("destinationId")]
+    public string? DestinationId { get; set; }
+
+    [JsonPropertyName("submissionId")]
+    public string? SubmissionId { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmissionReducedDto.cs b/FitConnect/Services/Models/v1/Submission/SubmissionReducedDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e98939b1b72de6f74467e26116e9ec0e978842c7
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmissionReducedDto.cs
@@ -0,0 +1,27 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmissionReducedDto {
+    [JsonPropertyName("serviceType")]
+    private ServiceTypeDto _serviceTypeDto { get; set; }
+
+
+    [JsonPropertyName("attachments")]
+    private List<string>? Attachments { get; set; }
+
+
+    [JsonPropertyName("callback")]
+    private CallbackDto? Callback { get; set; }
+
+
+    [JsonPropertyName("caseId")]
+    private string? CaseId { get; set; }
+
+    [JsonPropertyName("destinationId")]
+    private string? DestinationId { get; set; }
+
+
+    [JsonPropertyName("submissionId")]
+    private string? SubmissionId { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmissionsForPickupDto.cs b/FitConnect/Services/Models/v1/Submission/SubmissionsForPickupDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a6e98a2320c872a4f5f98e924697eefd2778a0a8
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmissionsForPickupDto.cs
@@ -0,0 +1,17 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmissionsForPickupDto {
+    [JsonPropertyName("count")]
+    public int Count { get; set; }
+
+    [JsonPropertyName("offset")]
+    public int Offset { get; set; }
+
+    [JsonPropertyName("submissions")]
+    public List<SubmissionForPickupDto>? Submissions { get; set; }
+
+    [JsonPropertyName("totalCount")]
+    public long TotalCount { get; set; }
+}
diff --git a/FitConnect/Services/Models/v1/Submission/SubmitSubmissionDto.cs b/FitConnect/Services/Models/v1/Submission/SubmitSubmissionDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b5e206ca846215e34fb4c12c3490793f7f905c7f
--- /dev/null
+++ b/FitConnect/Services/Models/v1/Submission/SubmitSubmissionDto.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+
+namespace FitConnect.Services.Models.v1.Submission;
+
+public class SubmitSubmissionDto {
+    [JsonPropertyName("encryptedData")]
+    public string? EncryptedData { get; set; }
+
+    [JsonPropertyName("encryptedMetadata")]
+    public string? EncryptedMetadata { get; set; }
+}
diff --git a/FitConnect/Services/OAuthService.cs b/FitConnect/Services/OAuthService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bef055daa97122340cd26fe332fa9315d4af4abc
--- /dev/null
+++ b/FitConnect/Services/OAuthService.cs
@@ -0,0 +1,82 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Security.Authentication;
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.Services;
+
+public class OAuthService : RestCallService, IOAuthService {
+    private readonly string _clientId;
+    private readonly string _clientSecret;
+    private readonly ILogger? _logger;
+    private readonly string _tokenUrl;
+    private OAuthAccessToken? _token;
+
+    public OAuthService(string tokenUrl, string version, string clientId, string clientSecret,
+        ILogger? logger) : base($"{tokenUrl}/{version}", logger) {
+        _tokenUrl = tokenUrl;
+        _clientId = clientId;
+        _clientSecret = clientSecret;
+        _logger = logger;
+    }
+
+    public OAuthAccessToken? Token {
+        get => _token;
+        private set {
+            _token = value;
+            AccessToken = value?.AccessToken;
+        }
+    }
+
+    /// <summary>
+    ///     Requesting an OAuth token from the FitConnect API.
+    ///     <para>
+    ///         You can get the Client ID and Client Secret from the FitConnect Self Service portal
+    ///         under <br />
+    ///         https://portal.auth-testing.fit-connect.fitko.dev
+    ///     </para>
+    /// </summary>
+    /// <param name="clientId">Your client Id</param>
+    /// <param name="clientSecret">Your client Secret</param>
+    /// <param name="scope">Scope if needed</param>
+    /// <returns>The received token or null</returns>
+    public async Task AuthenticateAsync(
+        string? scope = null) {
+        var client = CreateClient();
+
+        var requestContent = new Dictionary<string, string> {
+            { "grant_type", "client_credentials" },
+            { "client_id", _clientId },
+            { "client_secret", _clientSecret }
+        };
+
+        if (scope != null)
+            requestContent["scope"] = scope;
+
+        var content = new FormUrlEncodedContent(requestContent);
+
+        var request = new HttpRequestMessage(HttpMethod.Post, _tokenUrl) {
+            Content = content,
+            Method = HttpMethod.Post
+        };
+
+        var response = await client.SendAsync(request);
+        if (response.IsSuccessStatusCode) {
+            var result = await response.Content.ReadFromJsonAsync<OAuthAccessToken>();
+            if (result == null)
+                throw new AuthenticationException("Failed to authenticate");
+            Token = result;
+        }
+
+        if (response.StatusCode == HttpStatusCode.Unauthorized)
+            throw new InvalidCredentialException(await response.Content.ReadAsStringAsync());
+    }
+
+    public bool IsAuthenticated => Token != null;
+
+    public void EnsureAuthenticated() {
+        if (!IsAuthenticated) AuthenticateAsync().Wait();
+    }
+}
diff --git a/FitConnect/Services/RestCallService.cs b/FitConnect/Services/RestCallService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..90a572003c24d2b07f6069df412a41d8dddecdcc
--- /dev/null
+++ b/FitConnect/Services/RestCallService.cs
@@ -0,0 +1,98 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.Services;
+
+public interface IRestCallService {
+    public WebProxy? Proxy { get; set; }
+}
+
+public abstract class RestCallService : IRestCallService {
+    private readonly string _baseUrl;
+    private readonly ILogger? _logger;
+
+
+    protected RestCallService(string baseUrl, ILogger? logger = null) {
+        _baseUrl = baseUrl;
+        _logger = logger;
+    }
+
+    protected static string? AccessToken { get; set; }
+    public WebProxy? Proxy { get; set; }
+
+    protected HttpClient CreateClient() {
+        var clientHandler = new HttpClientHandler {
+            Proxy = Proxy
+        };
+        var client = new HttpClient(clientHandler);
+        client.DefaultRequestHeaders.Add("Accept", "application/json");
+        if (AccessToken != null)
+            client.DefaultRequestHeaders.Add("Authorization", $"Bearer {AccessToken}");
+        return client;
+    }
+
+    protected async Task<string> RestCallForString(string endpoint, HttpMethod method,
+        string? body = null, string contentType = "application/json",
+        string accept = "application/json") {
+        var client = CreateClient();
+
+        var request = new HttpRequestMessage();
+
+        if (accept != "application/json") {
+            request.Headers.Accept.Clear();
+            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
+        }
+
+
+        request.Method = method;
+
+        request.RequestUri = new Uri($"{_baseUrl}{endpoint}");
+
+        if (body != null) {
+            request.Content = new StringContent(body, Encoding.UTF8, contentType);
+            _logger?.LogTrace("Body: {Body}", body);
+        }
+
+        var response = await client.SendAsync(request);
+        _logger?.LogDebug("Server call: {Method} {Uri} -  {StatusCode}", method, request.RequestUri,
+            response.StatusCode);
+
+        if (response.IsSuccessStatusCode) {
+            var content = await response.Content.ReadAsStringAsync();
+            _logger?.LogTrace("Response: {Content}", content);
+            return content;
+        }
+
+        _logger?.LogError("Error calling {Method} - {Endpoint} with {Body} => {Status}", method,
+            endpoint,
+            body, response.StatusCode);
+        throw new HttpRequestException("Error calling FitConnect API",
+            new HttpRequestException(
+                $"{method} -  {request.RequestUri.ToString()} - {response.StatusCode}\n" +
+                await response.Content.ReadAsStringAsync()));
+    }
+
+    protected async Task<T?> RestCall<T>(string endpoint, HttpMethod method, string? body = null,
+        string contentType = "application/json", string accept = "application/json")
+        where T : class {
+        var content = await RestCallForString(endpoint, method, body, contentType, accept);
+        try {
+            var result = JsonSerializer.Deserialize<T>(content);
+
+            if (result != null)
+                return result;
+        }
+        catch (JsonException e) {
+            _logger?.LogError("Error deserializing content from {Uri}\r\n{Content}",
+                $"{_baseUrl}{endpoint}",
+                content);
+            throw new HttpRequestException("Error deserializing FitConnect API response",
+                e);
+        }
+
+        throw new ArgumentException("Can not deserialize response to type " + typeof(T).Name);
+    }
+}
diff --git a/FitConnect/Services/RouteService.cs b/FitConnect/Services/RouteService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e1673d0cffa2208cdeec420a431a8cf4e2e146f3
--- /dev/null
+++ b/FitConnect/Services/RouteService.cs
@@ -0,0 +1,47 @@
+using FitConnect.Models;
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models.v1.Routes;
+using Microsoft.Extensions.Logging;
+using Route = FitConnect.Services.Models.v1.Routes.Route;
+
+namespace FitConnect.Services;
+
+public class RouteService : RestCallService, IRouteService {
+    private readonly IOAuthService _oAuthService;
+
+    public RouteService(string baseUrl, IOAuthService oAuthService, string version = "v1",
+        ILogger? logger = null) : base($"{baseUrl}/{version}", logger) {
+        _oAuthService = oAuthService;
+    }
+
+    /// <summary>
+    ///     Returns the destination id for the given intent.
+    /// </summary>
+    /// <param name="leikaKey"></param>
+    /// <param name="ags"></param>
+    /// <param name="ars"></param>
+    /// <param name="areaId"></param>
+    /// <returns></returns>
+        public async Task<List<Route>> GetDestinationIdAsync(string leikaKey,
+        string? ags, string? ars,
+        string? areaId) {
+        if (ars == null && ags == null && areaId == null)
+            throw new ArgumentException("Either ars, ags or areaId must be specified.");
+
+        var result = await RestCall<RoutesListDto>($"/routes?leikaKey={leikaKey}" +
+                                                   (ags != null ? $"&ags={ags}" : "") +
+                                                   (ars != null ? $"&ars={ars}" : "") +
+                                                   (areaId != null ? $"&areaId={areaId}" : ""),
+            HttpMethod.Get);
+
+        return result?.Routes?.ToList() ?? new List<Route>();
+    }
+
+    public async Task<AreaList?> GetAreas(string filter, int offset = 0, int limit = 100) {
+        var result = await RestCall<AreaList>(
+            $"/areas?areaSearchexpression={filter}&offset={offset}&limit={limit}",
+            HttpMethod.Get);
+
+        return result;
+    }
+}
diff --git a/FitConnect/Services/SubmissionService.cs b/FitConnect/Services/SubmissionService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..198106f82d119ab73cf2fd0b9554160bd03e7eee
--- /dev/null
+++ b/FitConnect/Services/SubmissionService.cs
@@ -0,0 +1,147 @@
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models.v1.Case;
+using FitConnect.Services.Models.v1.Submission;
+using Microsoft.Extensions.Logging;
+
+namespace FitConnect.Services;
+
+public class SubmissionService : RestCallService, ISubmissionService {
+    private readonly ILogger? _logger;
+    private readonly IOAuthService _oAuthService;
+
+    public SubmissionService(string baseUrl, IOAuthService oAuthService,
+        string version = "v1", ILogger? logger = null) : base(
+        $"{baseUrl}/{version}", logger) {
+        _oAuthService = oAuthService;
+        _logger = logger;
+    }
+
+    /// <summary>
+    ///     <para>@PostMapping("/submissions")</para>
+    /// </summary>
+    /// <param name="submissionDto">RequestBody</param>
+    /// <returns></returns>
+        public SubmissionCreatedDto CreateSubmission(CreateSubmissionDto submissionDto) {
+        _oAuthService.EnsureAuthenticated();
+        var result = RestCall<SubmissionCreatedDto>(
+            "/submissions",
+            HttpMethod.Post,
+            JsonSerializer.Serialize(submissionDto)).Result;
+
+        if (result == null)
+            throw new ArgumentException("Submission creation failed");
+
+        return result;
+    }
+
+
+    /// <summary>
+    ///     <para>
+    ///         @PutMapping(value = "/submissions/{submissionId}/attachments/{attachmentId}", consumes =
+    ///         "application/jose")
+    ///     </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="attachmentId">PathVariable</param>
+    /// <param name="encryptedAttachmentContent">RequestBody</param>
+    /// <returns></returns>
+        public bool AddSubmissionAttachment(string submissionId, string attachmentId,
+        string encryptedAttachmentContent) {
+        _oAuthService.EnsureAuthenticated();
+        var result = RestCallForString(
+            $"/submissions/{submissionId}/attachments/{attachmentId}",
+            HttpMethod.Put,
+            encryptedAttachmentContent,
+            "application/jose").Result;
+        return true;
+    }
+
+    /// <summary>
+    ///     <para>@PutMapping(value = "/submissions/{submissionId}", consumes = "application/json") </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="submitSubmission">RequestBody</param>
+    /// <returns></returns>
+        public async Task<SubmissionReducedDto?> SubmitSubmission(string submissionId,
+        SubmitSubmissionDto submitSubmission) {
+        _oAuthService.EnsureAuthenticated();
+        var body = JsonSerializer.Serialize(submitSubmission, new JsonSerializerOptions {
+            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+            WriteIndented = true,
+            Encoder = JavaScriptEncoder.Default
+        });
+
+        var result = await RestCall<SubmissionReducedDto>($"/submissions/{submissionId}",
+            HttpMethod.Put,
+            body);
+
+        return result;
+    }
+
+
+    /// <summary>
+    ///     <para>@GetMapping("/submissions")</para>
+    /// </summary>
+    /// <param name="destinationId">RequestParam</param>
+    /// <param name="offset">RequestParam</param>
+    /// <param name="limit">RequestParam</param>
+    /// <returns></returns>
+        public async Task<SubmissionsForPickupDto> ListSubmissions(string? destinationId,
+        int offset = 0,
+        int limit = 100) {
+        _oAuthService.EnsureAuthenticated();
+        var url = destinationId != null
+            ? $"/submissions?destinationId={destinationId}&offset={offset}&limit={limit}"
+            : $"/submissions?offset={offset}&limit={limit}";
+        return RestCall<SubmissionsForPickupDto>(url, HttpMethod.Get).Result ??
+               new SubmissionsForPickupDto();
+    }
+
+    /// <summary>
+    ///     <para>@GetMapping("/submissions/{submissionId}")</para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <returns></returns>
+        public SubmissionDto GetSubmission(string submissionId) {
+        _oAuthService.EnsureAuthenticated();
+        var result =
+            RestCall<SubmissionDto>($"/submissions/{submissionId}", HttpMethod.Get).Result ??
+            new SubmissionDto();
+
+        return result;
+    }
+
+
+    /// <summary>
+    ///     <para>
+    ///         @GetMapping(value = "/submissions/{submissionId}/attachments/{attachmentId}", produces =
+    ///         "application/jose")
+    ///     </para>
+    /// </summary>
+    /// <param name="submissionId">PathVariable</param>
+    /// <param name="attachmentId">PathVariable</param>
+    /// <returns></returns>
+        public string GetAttachment(string submissionId, string attachmentId) {
+        _oAuthService.EnsureAuthenticated();
+        var result = RestCallForString($"/submissions/{submissionId}/attachments/{attachmentId}",
+            HttpMethod.Get,
+            null,
+            "application/jose",
+            "application/jose").Result;
+
+        return result;
+    }
+
+    public string GetKey(string keyId) {
+        throw new NotImplementedException();
+    }
+
+    public async Task<List<string>> GetStatusForSubmissionAsync(string caseId) {
+        _oAuthService.EnsureAuthenticated();
+        var events = await RestCall<EventLogDto>($"/cases/{caseId}/events", HttpMethod.Get);
+        return events.EventLog.ToList();
+    }
+}
diff --git a/FitConnect/Subscriber.cs b/FitConnect/Subscriber.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fcdd076df1df9e11f0efb0ec942da75d99b0a073
--- /dev/null
+++ b/FitConnect/Subscriber.cs
@@ -0,0 +1,208 @@
+using System.Reflection;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Interfaces.Subscriber;
+using FitConnect.Models;
+using FitConnect.Models.v1.Api;
+using FitConnect.Services.Models.v1.Submission;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using NJsonSchema;
+using NJsonSchema.Validation;
+using Metadata = FitConnect.Models.Api.Metadata.Metadata;
+
+namespace FitConnect;
+
+/// <summary>
+///     Fluent API for the FitConnect.Subscriber
+/// </summary>
+public class Subscriber : FitConnectClient,
+    ISubscriber,
+    ISubscriberWithSubmission {
+    public Subscriber(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        string privateKeyDecryption,
+        string privateKeySigning,
+        string publicKeyEncryption,
+        string publicKeySignatureVerification,
+        IContainer container) : base(environment, clientId, clientSecret,
+        container,
+        privateKeyDecryption, privateKeySigning, publicKeyEncryption,
+        publicKeySignatureVerification) {
+        Encryption = new FitEncryption(privateKeyDecryption, privateKeySigning, publicKeyEncryption,
+            publicKeySignatureVerification,
+            container.Resolve<ILogger>());
+    }
+
+    public Subscriber(FitConnectEnvironment environment,
+        string clientId, string clientSecret,
+        string privateKeyDecryption,
+        string privateKeySigning,
+        string publicKeyEncryption,
+        string publicKeySignatureVerification,
+        ILogger? logger = null) : base(environment, clientId, clientSecret, logger,
+        privateKeyDecryption, privateKeySigning) {
+        Encryption = new FitEncryption(privateKeyDecryption, privateKeySigning, publicKeyEncryption,
+            publicKeySignatureVerification, logger);
+    }
+
+
+    /// <summary>
+    ///     Receives the available submissions from the server
+    /// </summary>
+    /// <param name="destinationId"></param>
+    /// <param name="skip"></param>
+    /// <param name="take"></param>
+    /// <returns></returns>
+    public IEnumerable<SubmissionForPickupDto> GetAvailableSubmissions(string? destinationId = null,
+        int skip = 0,
+        int take = 100) {
+        var submissionsResult = SubmissionService.ListSubmissions(destinationId, 0, 100).Result;
+
+        // Creating a dictionary of destinationId to submissionIds from the REST API result
+        return submissionsResult.Submissions;
+    }
+
+
+    /// <summary>
+    ///     Receives a specific submission from the server.
+    /// </summary>
+    /// <param name="submissionId"></param>
+    /// <param name="skipSchemaTest"></param>
+    /// <returns></returns>
+    public ISubscriberWithSubmission RequestSubmission(string? submissionId,
+        bool skipSchemaTest = false) {
+        if (submissionId == null) {
+            throw new ArgumentNullException($"{nameof(submissionId)} has to be set");
+        }
+
+        var submission = (Submission)SubmissionService.GetSubmission(submissionId);
+        var (metaDataString, _, metaHash) = Encryption.Decrypt(submission.EncryptedMetadata!);
+
+        if (!skipSchemaTest) {
+            var errors = VerifyMetadata(metaDataString);
+            if (errors.Count > 0) {
+                Logger?.LogWarning("Invalid metadata: {MetaData}", metaDataString);
+                foreach (var error in errors) Logger?.LogError("Error: {Error}", error.ToString());
+
+                throw new Exception($"Metadata validation failed: {string.Join(", ", errors)}");
+            }
+        }
+
+        submission.Metadata = JsonConvert.DeserializeObject<Metadata>(metaDataString);
+
+
+        if (submission.EncryptedData != null) {
+            var (dataString, _, dataHash) = Encryption.Decrypt(submission.EncryptedData);
+            submission.Data = dataString;
+
+            if (submission?.Metadata?.ContentStructure.Data.Hash.Content !=
+                FitEncryption.CalculateHash(dataString)) {
+                Logger?.LogWarning("Data hash mismatch: {DataHash} != {CalculatedHash}",
+                    submission?.Metadata?.ContentStructure.Data.Hash.Content,
+                    FitEncryption.CalculateHash(dataString));
+                throw new Exception("Data hash mismatch");
+            }
+        }
+
+        Submission = submission;
+        return this;
+    }
+
+    public Submission? Submission { get; private set; }
+
+    /// <summary>
+    ///     Reading attachments for a submission.
+    /// </summary>
+    /// <returns></returns>
+    public IEnumerable<Attachment> GetAttachments() {
+        // TODO add guard calls
+
+        var attachments = new List<Attachment>();
+        foreach (var id in Submission!.AttachmentIds) {
+            var encryptedAttachment = SubmissionService.GetAttachment(Submission.Id, id);
+            var (_, content, hash) = Encryption.Decrypt(encryptedAttachment);
+            var attachmentMeta =
+                Submission.Metadata.ContentStructure.Attachments.First(a => a.AttachmentId == id);
+
+            attachments.Add(new Attachment(attachmentMeta, content));
+        }
+
+        Submission.Attachments = attachments;
+        return attachments;
+    }
+
+    public void AcceptSubmission() {
+        CompleteSubmission(Submission!, FinishSubmissionStatus.Accepted);
+    }
+
+    public void RejectSubmission(params Problems[] problems) {
+        CompleteSubmission(Submission!, FinishSubmissionStatus.Rejected, problems);
+    }
+
+    public void ForwardSubmission() {
+        CompleteSubmission(Submission!, FinishSubmissionStatus.Forwarded);
+    }
+
+    public void CompleteSubmission(FinishSubmissionStatus status) {
+        CompleteSubmission(Submission!, status);
+    }
+
+
+    /// <summary>
+    ///     Verify the metadata hash and content to fit the schema
+    /// </summary>
+    /// <param name="metadataString"></param>
+    /// <returns></returns>
+    public static ICollection<ValidationError> VerifyMetadata(string metadataString) {
+        var schemaString = LoadContentOfResource("metadata.schema.json");
+        var schema = JsonSchema.FromJsonAsync(schemaString).Result;
+        return schema.Validate(metadataString);
+    }
+
+    private static string LoadContentOfResource(string resourceName) {
+        var assembly = Assembly.GetExecutingAssembly();
+        var fullQualifiedName = $"{assembly.GetName().Name}.{resourceName}";
+        var resourceStream = assembly.GetManifestResourceStream(fullQualifiedName);
+        var reader = new StreamReader(resourceStream);
+        return reader.ReadToEnd();
+    }
+
+
+    private string GetEvent(FinishSubmissionStatus state) {
+        return state switch {
+            FinishSubmissionStatus.Accepted =>
+                "https://schema.fitko.de/fit-connect/events/accept-submission",
+            FinishSubmissionStatus.Rejected =>
+                "https://schema.fitko.de/fit-connect/events/reject-submission",
+            FinishSubmissionStatus.Forwarded =>
+                "https://schema.fitko.de/fit-connect/events/forward-submission",
+            _ => throw new ArgumentException("Invalid state")
+        };
+    }
+
+    public void CompleteSubmission(SubmissionForPickupDto submission,
+        FinishSubmissionStatus status, Problems[]? problems = null) {
+        if (submission.SubmissionId == null || submission.CaseId == null ||
+            submission.DestinationId == null)
+            throw new ArgumentException("Submission does not contain all required fields");
+
+        if (status != FinishSubmissionStatus.Rejected && problems != null)
+            throw new ArgumentException("Problems can only be set for rejected submissions");
+
+        var eventName = GetEvent(status);
+
+        var token =
+            Encryption.CreateSecurityEventToken(submission.SubmissionId, submission.CaseId,
+                submission.DestinationId, eventName, problems);
+
+        var result = CasesService.FinishSubmission(submission.CaseId, token);
+        Logger?.LogInformation("Submission completed {status}", result);
+    }
+}
+
+public enum FinishSubmissionStatus {
+    Accepted,
+    Rejected,
+    Forwarded
+}
diff --git a/FitConnect/metadata.schema.json b/FitConnect/metadata.schema.json
index 0f730cd32e5411cf4d8d898e589307102f9c40b5..1d07dc235c85b1735ce09a6323dbe9a464906fed 100644
--- a/FitConnect/metadata.schema.json
+++ b/FitConnect/metadata.schema.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "$schema": "https://json-schema.org/draft-07/schema",
   "$id": "https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json",
   "type": "object",
   "title": "Metadaten",
diff --git a/IntegrationTests/HelperMethods.cs b/IntegrationTests/HelperMethods.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e076c7014e78d6d0d189afeb49a04a98b34ce707
--- /dev/null
+++ b/IntegrationTests/HelperMethods.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+
+namespace IntegrationTests;
+
+public static class HelperMethods {
+    public static (string id, string secret) GetSecrets() {
+        // relative to the project execution directory
+        const string secretFile = "../../../http-client.env.json";
+
+        if (!File.Exists(secretFile)) {
+            // If the secret file is not found, create it with the default values
+            // The file will be pretty in C#11, when """ is introduced
+            File.WriteAllText(secretFile, @"
+{
+    ""sender"": {
+        ""id"": ""00000000-0000-0000-0000-000000000000"",
+        ""secret"": ""0000000000000000000000000000000000000000000"",
+        ""scope"": ""send:region:DE""
+    }
+}");
+            throw new Exception("Please fill the secret.json file with your sender credentials");
+        }
+
+        var jsonContent = File.ReadAllText(secretFile);
+        var secret = JsonConvert.DeserializeObject<dynamic>(jsonContent);
+        return (secret.sender.id, secret.sender.secret);
+    }
+}
diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..d95a0c10813bbec26c5f71ac327cc1d9ab2f2030
--- /dev/null
+++ b/IntegrationTests/IntegrationTests.csproj
@@ -0,0 +1,32 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="DotNet.Testcontainers" Version="1.6.0" />
+        <PackageReference Include="FluentAssertions" Version="6.7.0" />
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
+        <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.21.0" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+        <PackageReference Include="NUnit" Version="3.13.2" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
+        <PackageReference Include="coverlet.collector" Version="3.1.0" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\FitConnect\FitConnect.csproj" />
+        <ProjectReference Include="..\MockContainer\MockContainer.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <None Update="Test.pdf">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+
+</Project>
diff --git a/IntegrationTests/OAuthServiceTest.cs b/IntegrationTests/OAuthServiceTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6f1f29fd860b4e9b0231ddba5f56eced4f2c9144
--- /dev/null
+++ b/IntegrationTests/OAuthServiceTest.cs
@@ -0,0 +1,53 @@
+using System.Security.Authentication;
+using FitConnect.Models;
+using FitConnect.Services;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace IntegrationTests;
+
+public class OAuthServiceTest {
+    private string _clientId;
+    private string _clientSecret;
+    private OAuthService _oAuthService = null!;
+
+    [OneTimeSetUp]
+    public void OneTimeSetup() {
+        (_clientId, _clientSecret) = HelperMethods.GetSecrets();
+    }
+
+
+    [SetUp]
+    public void SetUp() {
+        var endpoints = FitConnectEnvironment.Testing;
+        _oAuthService = new OAuthService(endpoints.TokenUrl, "v1", _clientId, _clientSecret, null);
+    }
+
+    [Test]
+    public void GetAccessToken_ExpiresInShouldBe1800_WithoutScope() {
+        _oAuthService.AuthenticateAsync().Wait();
+        var token = _oAuthService.Token;
+        token.Should().NotBeNull();
+        token!.ExpiresIn.Should().Be(1800);
+        token.Scope.Should().Be("send:region:DE");
+    }
+
+    [Test]
+    public void GetAccessToken_WrongCredentials_ThrowInvalidCredentialException() {
+        var exception = Assert.ThrowsAsync<InvalidCredentialException>(() =>
+            new OAuthService(FitConnectEnvironment.Testing.TokenUrl, "v1", "wrong", "wrong",
+                    null)
+                .AuthenticateAsync()
+        );
+        exception!.Message.Replace(" ", "").Should().Contain("\"error\":\"invalid_client\"");
+    }
+
+    [Test]
+    public void GetAccessToken_ScopeShouldMatch_WithScope() {
+        _oAuthService.AuthenticateAsync("send:region:DE01010").Wait();
+        var token = _oAuthService.Token;
+        token.Should().NotBeNull();
+        token!.ExpiresIn.Should().Be(1800);
+        token.Scope.Should().Be("send:region:DE01010");
+    }
+}
diff --git a/IntegrationTests/ProxyTest.cs b/IntegrationTests/ProxyTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7503e959b26a48d09255021fff5bfca11fa2b385
--- /dev/null
+++ b/IntegrationTests/ProxyTest.cs
@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Threading;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Containers;
+using FitConnect;
+using FitConnect.Models;
+using FitConnect.Services;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace IntegrationTests;
+
+[Ignore("Not testable in docker container, take to long to run every time")]
+[TestFixture]
+public class ProxyTest {
+    [OneTimeSetUp]
+    public void OneTimeSetup() {
+        var path = $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}/proxy";
+        Console.WriteLine($"Creating directory: {path}");
+        Directory.CreateDirectory(path);
+        (_id, _secret) = HelperMethods.GetSecrets();
+        File.WriteAllText("proxy/access.log", "");
+
+        _container = new TestcontainersBuilder<TestcontainersContainer>()
+            .WithImage("ubuntu/squid")
+            .WithPortBinding("3128", "3128")
+            .WithBindMount(path, @"/var/log/squid")
+            .Build();
+        _container.StartAsync().Wait();
+        Thread.Sleep(5000);
+    }
+
+    [SetUp]
+    public void Setup() {
+        sender =
+            new FitConnect.Sender(FitConnectEnvironment.Testing,
+                    _id,
+                    _secret)
+                .WithProxy("localhost", 3128);
+
+        _webClient = new WebClient {
+            Proxy =
+                new WebProxy("http://localhost:3128")
+        };
+        _webClient.DownloadString("https://www.fitko.de/");
+    }
+
+
+    [TearDown]
+    public void TearDown() {
+        _webClient.DownloadString("https://www.google.de/").Should().NotBeNull();
+    }
+
+    [OneTimeTearDown]
+    public void OneTimeTearDown() {
+        Thread.Sleep(10000);
+        _container.StopAsync().Wait();
+        var content = File.ReadAllText("proxy/access.log");
+        Console.WriteLine(content);
+        content.Should().Contain("auth-testing.fit-connect.fitko.dev:443");
+    }
+
+    private WebClient _webClient = null!;
+
+    private TestcontainersContainer _container = null!;
+    private string _id = null!;
+    private string _secret = null!;
+    private FitConnectClient sender = null!;
+
+    [Test]
+    [Order(1)]
+    public void ContainerIsRunning() {
+        sender.GetProxy()!.Address.Should().Be("http://localhost:3128");
+        _container.Should().NotBeNull();
+        _container.State.Should().Be(TestcontainersState.Running);
+    }
+
+    [Test]
+    [Order(10)]
+    public void SendSubmissionViaProxy_None_Work() {
+        var sender = new FitConnect.Sender(
+                FitConnectEnvironment.Testing, "", "")
+            .WithProxy("localhost", 3128);
+
+        (sender as FitConnect.Sender).OAuthService.Proxy.Should().NotBeNull();
+    }
+
+    [Test]
+    [Order(20)]
+    public void RequestOAuthToken() {
+        // Arrange
+        var testUrl = FitConnectEnvironment.Testing
+            .TokenUrl;
+
+        var oAuthService = new OAuthService(testUrl, "v1", _id, _secret, null) {
+            Proxy = new WebProxy("http://localhost:3128")
+        };
+
+        // Act
+        oAuthService.AuthenticateAsync().Wait();
+        var token = oAuthService.Token;
+
+        // Assert
+        token.Should().NotBeNull();
+    }
+}
diff --git a/IntegrationTests/Sender/SenderTestBase.cs b/IntegrationTests/Sender/SenderTestBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6ba21abe72cbf05d2f5a2dc0afffb5632560e587
--- /dev/null
+++ b/IntegrationTests/Sender/SenderTestBase.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Linq;
+using FitConnect.Interfaces.Sender;
+using FitConnect.Models;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace IntegrationTests.Sender;
+
+[Order(100)]
+[TestFixture]
+public abstract class SenderTestBase {
+    protected string LeikaKey = "";
+    
+    /// <summary>
+    ///     Setup creates json file for credentials if not found
+    /// </summary>
+    /// <exception cref="Exception"></exception>
+    [OneTimeSetUp]
+    public void OneTimeSetup() {
+        // relative to the project execution directory
+        const string secretFile = "../../../http-client.env.json";
+
+        if (!File.Exists(secretFile)) {
+            // If the secret file is not found, create it with the default values
+            // The file will be pretty in C#11, when """ is introduced
+            File.WriteAllText(secretFile, @"
+{
+    ""sender"": {
+        ""id"": ""00000000-0000-0000-0000-000000000000"",
+        ""secret"": ""0000000000000000000000000000000000000000000"",
+        ""scope"": ""send:region:DE""
+    }
+}");
+            throw new Exception("Please fill the secret.json file with your sender credentials");
+        }
+
+        var jsonContent = File.ReadAllText(secretFile);
+        var secret = JsonConvert.DeserializeObject<dynamic>(jsonContent);
+        _clientId = secret.sender.id;
+        _clientSecret = secret.sender.secret;
+    }
+
+    [SetUp]
+    public void Setup() {
+        var logger = LoggerFactory.Create(b => b.AddConsole()).CreateLogger<FitConnect.Sender>();
+        Sender = new FitConnect.Sender(
+            FitConnectEnvironment.Testing,
+            _clientId, _clientSecret, logger);
+    }
+
+    protected const string desitnationId = "aa3704d6-8bd7-4d40-a8af-501851f93934";
+    protected string _clientId = "73a8ff88-076b-4263-9a80-8ebadac97b0d";
+    protected string _clientSecret = "rdlXms-4ikO47AbTmmCTdzFoE4cTSt13JmSbcY5Dhsw";
+    protected ISender Sender;
+
+    protected Submission GetSubmissionInfo(ISender sender) {
+        var submission = sender.GetType().GetProperties()
+            .FirstOrDefault(p => p.Name == "Submission")
+            .GetValue(Sender) as Submission;
+
+        return submission;
+    }
+}
diff --git a/IntegrationTests/Sender/SenderTestHappyPath.cs b/IntegrationTests/Sender/SenderTestHappyPath.cs
new file mode 100644
index 0000000000000000000000000000000000000000..44612373f725f6a6a3ba45a6614032b24c1f8ec1
--- /dev/null
+++ b/IntegrationTests/Sender/SenderTestHappyPath.cs
@@ -0,0 +1,192 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using FitConnect.Models;
+using FluentAssertions;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace IntegrationTests.Sender;
+
+[TestFixture]
+public class SenderTestHappyPath : SenderTestBase {
+    [Test]
+    public void CheckIfSecretsAreValid() {
+        _clientId.Should().NotBe("00000000-0000-0000-0000-000000000000");
+        _clientSecret.Should().NotBe("0000000000000000000000000000000000000000000");
+    }
+
+    [Test]
+    public void WithDestination_PrepareSubmissionToServer() {
+        // Arrange
+
+        // Act
+        Sender.WithDestination(desitnationId);
+
+        // Assert
+        Sender.PublicKey.Should().NotBeNullOrEmpty();
+    }
+
+    [Test]
+    public void WithAttachments_IntroduceSubmission_ShouldGetIdFromServer() {
+        // Arrange
+        var dut = Sender
+            .WithDestination(desitnationId)
+            .WithServiceType("ServiceName", "urn:de:fim:leika:leistung:99400048079000");
+
+        var attachments = new List<Attachment>();
+        var attachment = new Attachment("Test.pdf", "Just an attachment");
+
+        attachments.Add(attachment);
+
+        // {'attachmentId': 'c96cce5b-f35d-4c38-ac19-9bc47721c0f9', 'hash': {'type': 'sha512', 'content': '8b1042900c2039f65fe6c4cb1bca31e2a7a04b61d3ca7d9ae9fc4077068b82ad5512fa298385b025db70551113b762064444b87737e45e657a71be5b88b06e59'}, 'mimeType': 'application/pdf', 'purpose': 'attachment'}
+
+
+        // Act
+        dut.WithAttachments(attachments);
+
+
+        // Assert
+        attachment.Hash.Should()
+            .Be(
+                "8b1042900c2039f65fe6c4cb1bca31e2a7a04b61d3ca7d9ae9fc4077068b82ad5512fa298385b025db70551113b762064444b87737e45e657a71be5b88b06e59");
+        var submission = GetSubmissionInfo(Sender);
+        submission.Attachments.Should().HaveCount(attachments.Count);
+
+        submission.Id.Should().NotBeNullOrWhiteSpace();
+        Console.WriteLine(submission.CaseId);
+    }
+
+
+    [TestCase("0b8e6fbd-62e2-4b6f-b333-5308d82e0a00")]
+    [TestCase("d2be2027-9368-4c0c-a265-2fdbf7ecd4d9")]
+    public void GetSubmissionStatus(string caseId) {
+        var status = Sender.GetStatusForSubmission(caseId);
+        status.ForEach(s => Console.WriteLine($"{s.EventTime} - {s.EventType}"));
+        status.Count.Should().BeGreaterThan(0);
+    }
+
+    [Test]
+    public void Submit_FinishSubmission_ShouldGetIdFromServer() {
+        // Arrange
+
+        var attachments = new List<Attachment>();
+
+        var attachment = new Attachment("Test.pdf", "Just an attachment");
+        attachments.Add(attachment);
+        var dut = Sender
+            .WithDestination(desitnationId)
+            .WithServiceType("ServiceName", "urn:de:fim:leika:leistung:99400048079000")
+            .WithAttachments(attachments);
+
+        // Act
+        dut.Submit();
+
+        // Assert
+        var submission = GetSubmissionInfo(Sender);
+        Console.WriteLine($"Case ID: {submission.CaseId}");
+        submission.Should().NotBeNull();
+    }
+
+    [Test]
+    public void Submit_FinishSubmissionWithData_ShouldGetIdFromServer() {
+        // Arrange
+
+        var attachments = new List<Attachment>();
+
+        var content = File.ReadAllBytes("Test.pdf");
+        var attachment = new Attachment("Test.pdf", "Just an attachment") {
+            Filename = "RandomBytes",
+            MimeType = "application/pdf",
+            Description = "Just a test",
+            Purpose = "attachment"
+        };
+        attachments.Add(attachment);
+        var dut = Sender
+            .WithDestination(desitnationId)
+            .WithServiceType("ServiceName", "urn:de:fim:leika:leistung:99400048079000")
+            .WithAttachments(attachments)
+            .WithData(JsonConvert.SerializeObject(new {
+                FirstName = "John",
+                LastName = "Doe",
+                Age = 42,
+                Birthday = DateTime.Today.AddYears(-42)
+            }));
+
+        // Act
+        dut.Submit();
+
+        // Assert
+        var submission = GetSubmissionInfo(Sender);
+        var statusForSubmission = Sender.GetStatusForSubmission(submission.CaseId);
+        foreach (var securityEventToken in statusForSubmission)
+            Console.WriteLine(securityEventToken.Token.Subject);
+
+        statusForSubmission.Should().HaveCountGreaterThan(0);
+        Console.WriteLine($"Case ID: {submission.CaseId}");
+        Console.WriteLine($"Submission ID: {submission.Id}");
+        submission.Should().NotBeNull();
+    }
+
+    [Ignore("Destination can not be found")]
+    [Test]
+    public void Submit_FinishSubmissionWithDataFindDestination_ShouldGetIdFromServer() {
+        // Arrange
+
+        var attachments = new List<Attachment>();
+
+        var content = File.ReadAllBytes("Test.pdf");
+        var attachment = new Attachment("Test.pdf", "Just an attachment") {
+            Filename = "RandomBytes",
+            MimeType = "application/pdf",
+            Description = "Just a test",
+            Purpose = "attachment"
+        };
+        attachments.Add(attachment);
+        var dut = Sender
+            .FindDestinationId("99400048079000", ars: "09372126")
+            .WithServiceType("ServiceName", "urn:de:fim:leika:leistung:99400048079000")
+            .WithAttachments(attachments)
+            .WithData(JsonConvert.SerializeObject(new {
+                FirstName = "John",
+                LastName = "Doe",
+                Age = 42,
+                Birthday = DateTime.Today.AddYears(-42)
+            }));
+
+        // Act
+        dut.Submit();
+
+        // Assert
+        var submission = GetSubmissionInfo(Sender);
+        var statusForSubmission = Sender.GetStatusForSubmission(submission.CaseId);
+        foreach (var securityEventToken in statusForSubmission)
+            Console.WriteLine(securityEventToken.Token.Subject);
+
+        statusForSubmission.Should().HaveCountGreaterThan(0);
+        Console.WriteLine($"Case ID: {submission.CaseId}");
+        Console.WriteLine($"Submission ID: {submission.Id}");
+        submission.Should().NotBeNull();
+    }
+
+    [Test]
+    public void GetAreas_ShouldGetAreasFromServer() {
+        // Arrange
+        var areas = Sender.GetAreas("Furth*", out var _);
+        areas.Should().HaveCountGreaterThan(0);
+    }
+
+    [Test]
+    public void GetDestinations_ShouldGetDestinationsFromServer() {
+        // Arrange
+        var destinations = Sender.FindDestinationId("99123456760610",
+            ars: "064350014014");
+        Sender.PublicKey.Should().NotBeNull(); //destinations.Should().HaveCountGreaterThan(0));
+
+        // foreach (var destination in destinations) {
+        //     destination.DestinationParameters.PublicKeys.Keys.ForEach(k =>
+        //         Console.WriteLine(k.ToString()));
+        //     destination.DestinationParameters.Status.Should().Be("active");
+        // }
+    }
+}
diff --git a/IntegrationTests/Sender/SenderTestUnhappyPath.cs b/IntegrationTests/Sender/SenderTestUnhappyPath.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fd6fdc47b1a3faab2f43bc1252169b10b32b800f
--- /dev/null
+++ b/IntegrationTests/Sender/SenderTestUnhappyPath.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Net.Http;
+using FitConnect.Models;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace IntegrationTests.Sender;
+
+[TestFixture]
+public class SenderTestUnhappyPath : SenderTestBase {
+    [Test]
+    public void WithDestination_UnknownUUID_ShouldThrowAggregateWithInnerHttpRequestException() {
+        Assert.Throws<AggregateException>(() => { Sender.WithDestination(Guid.Empty.ToString()); })
+            .InnerExceptions.Should().ContainItemsAssignableTo<HttpRequestException>();
+    }
+
+
+    [Test]
+    public void WithDestination_CompletelyWrong_ShouldThrowArgumentException() {
+        Assert.Throws<ArgumentException>(() => { Sender.WithDestination("This is very wrong"); })
+            .Message.Should().Be("The destination must be a valid GUID");
+    }
+
+    [Test]
+    public void WithData_DataIsInvalidJson_ShouldThrowArgumentException() {
+        Assert.Throws<ArgumentException>(() => {
+                Sender.WithDestination(desitnationId)
+                    .WithServiceType("Name", leikaKey: "urn:de:fim:leika:leistung:00000000000000")
+                    .WithAttachments(new Attachment("Test.pdf", "Test PDF"))
+                    .WithData("This is very wrong");
+            })
+            .Message.Should().Be("The data must be valid JSON string");
+    }
+}
diff --git a/IntegrationTests/Sender/ThreadTest.cs b/IntegrationTests/Sender/ThreadTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7e932904170ed4297c50436ee9a3188d6e83cc1c
--- /dev/null
+++ b/IntegrationTests/Sender/ThreadTest.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
+using Autofac;
+using FitConnect.Models;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace IntegrationTests.Sender;
+
+/// <summary>
+/// This test runs multi sender and one subscriber which "cleans" the server
+/// so other subscriber won't get any submissions. 
+/// </summary>
+[Ignore("Can not be combined with other tests - need to be run separately")]
+[TestFixture]
+public class ThreadTest {
+    private IContainer _container;
+    private MockSettings _setting;
+    private ILogger _logger;
+    private const int NumberOfThreads = 34;
+
+    [SetUp]
+    public void Setup() {
+        _container = MockContainer.Container.Create();
+        _setting = _container.Resolve<MockSettings>();
+        _logger = LoggerFactory.Create(b => b.AddConsole())
+            .CreateLogger<FitConnect.Sender>();
+    }
+
+    [Test]
+    [Order(1)]
+    public void ThreadedSender() {
+        var tasks = new List<Task>();
+
+        for (int i = 0; i < NumberOfThreads; i++) {
+            tasks.Add(Task.Run(() => {
+                var counter = Thread.CurrentThread.ManagedThreadId;
+                var Sender = new FitConnect.Sender(
+                    FitConnectEnvironment.Testing,
+                    _setting.SenderClientId, _setting.SenderClientSecret, _logger);
+
+                var delayed = Sender.WithDestination(_setting.DestinationId)
+                    .WithServiceType($"ThreadTest_{counter}", _setting.LeikaKey)
+                    .WithAttachments(new Attachment("Test.pdf", $"Attachment_{counter}"));
+
+                Thread.Sleep(RandomNumberGenerator.GetInt32(100, 500));
+                delayed
+                    .WithData(JsonConvert.SerializeObject(new {
+                        Name = $"ThreadTest_{counter}",
+                        ThreadId = counter
+                    }))
+                    .Submit();
+            }));
+        }
+
+        while (!tasks.All(t => t.IsCompleted)) {
+        }
+    }
+
+    [Test]
+    [Order(2)]
+    public void GetSubmissions() {
+        var subscriber = FitConnect.Client.GetSubscriber(FitConnectEnvironment.Testing,
+            _setting.SubscriberClientId, _setting.SubscriberClientSecret,
+            _setting.PrivateKeyDecryption, _setting.PrivateKeySigning,
+            _setting.PublicKeyEncryption, _setting.PublicKeySignatureVerification, _logger);
+
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+
+        foreach (var submission in submissions) {
+            subscriber
+                .RequestSubmission(submission.SubmissionId)
+                .AcceptSubmission();
+        }
+
+        submissions.Count.Should().Be(NumberOfThreads);
+    }
+}
diff --git a/IntegrationTests/Subscriber/SubscriberTestBase.cs b/IntegrationTests/Subscriber/SubscriberTestBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9b5dfa8863edc9d31f4073cebb872ba43ffd9d54
--- /dev/null
+++ b/IntegrationTests/Subscriber/SubscriberTestBase.cs
@@ -0,0 +1,55 @@
+using Autofac;
+using FitConnect.Interfaces.Subscriber;
+using FitConnect.Models;
+using Microsoft.Extensions.Logging;
+using MockContainer;
+using NUnit.Framework;
+
+namespace IntegrationTests.Subscriber;
+
+public abstract class SubscriberTestBase {
+    protected const string desitnationId = "aa3704d6-8bd7-4d40-a8af-501851f93934";
+    protected string _clientId = "20175c2b-c4dd-4a01-99b1-3a08436881a1";
+    protected string _clientSecret = "KV2qd7qc5n-xESB6dvfrTlMDx2BWHJd5hXJ6pKKnbEQ";
+    private IContainer _container;
+    protected ISubscriber subscriber { get; set; }
+    protected ILogger Logger;
+
+    [OneTimeSetUp]
+    public void OneTimeSetUp() {
+        // One-time initialization code
+        _container = Container.Create();
+    }
+
+    [SetUp]
+    public void SetUp() {
+        // Code here will be called before every test
+
+
+        Logger = LoggerFactory.Create(b => {
+                b.AddConsole();
+                b.SetMinimumLevel(LogLevel.Trace);
+            })
+            .CreateLogger<FitConnect.Sender>();
+        subscriber = new FitConnect.Subscriber(
+            FitConnectEnvironment.Testing,
+            _clientId,
+            _clientSecret,
+            _container.Resolve<MockSettings>().PrivateKeyDecryption,
+            _container.Resolve<MockSettings>().PrivateKeySigning,
+            _container.Resolve<MockSettings>().PublicKeyEncryption,
+            _container.Resolve<MockSettings>().PublicKeySignatureVerification,
+            Logger);
+    }
+
+
+    [TearDown]
+    public void TearsDown() {
+        // Code here will be called after every test
+    }
+
+    [OneTimeTearDown]
+    public void OneTimeTearDown() {
+        // One-time cleanup code
+    }
+}
diff --git a/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs b/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bfe0ed58a6adb48c12f44abc14dadec2fa55c889
--- /dev/null
+++ b/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs
@@ -0,0 +1,140 @@
+using System;
+using System.IO;
+using System.Linq;
+using FitConnect.Models.v1.Api;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+namespace IntegrationTests.Subscriber;
+
+[Order(200)]
+[TestFixture]
+public class SubscriberTestHappyPath : SubscriberTestBase {
+    [Order(201)]
+    [Test]
+    public void GetAvailableSubmissions_WithDestinationId_ShouldReturnSubmissionsForPickupDto() {
+        // Act
+        var submissions = subscriber.GetAvailableSubmissions(desitnationId).ToList();
+
+        // Assert
+        submissions.Count().Should().BeGreaterThanOrEqualTo(2);
+        submissions.All(s => s.DestinationId == desitnationId).Should().BeTrue();
+    }
+
+    [Order(202)]
+    [Test]
+    public void GetAvailableSubmissions_WithOutDestinationId_ShouldReturnSubmissionsForPickupDto() {
+        // Act
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+
+        // Assert
+        submissions.Count().Should().BeGreaterThan(0);
+    }
+
+
+    [Order(1001)]
+    [Test]
+    public void GetAllSubmission_WithSubmissionId_ShouldReturnSubmissionsForPickupDto() {
+        // Arrange
+        var errorCounter = 0;
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+        submissions.Count().Should().BeGreaterThan(0);
+        var i = 0;
+        foreach (var submissionId in submissions.Select(s => s.SubmissionId)) {
+            // Act
+            Console.WriteLine($"Getting submission {submissionId}");
+            var dto = subscriber.RequestSubmission(submissionId);
+
+            // Assert
+            errorCounter.Should().BeLessThan(submissions.Count());
+            Console.WriteLine($"Error counter: {errorCounter}/{submissions.Count()}");
+            if (i++ % 2 == 0)
+                dto.AcceptSubmission();
+            else
+                dto.RejectSubmission(new Problems() { Description = "A really critical problem" });
+        }
+    }
+
+    [Ignore("ID is already been used")]
+    [TestCase("303c60d3-3f6f-4913-bb51-4e66fe133c7f")]
+    public void GetAttachment_WithSubmissionId_ShouldReturnSubmissionsForPickupDto(
+        string submissionId) {
+        // Act
+        var attachments = subscriber.RequestSubmission(submissionId).GetAttachments();
+        foreach (var attachment in attachments) {
+            attachment.Content.Length.Should().BeGreaterThan(0);
+            File.WriteAllBytes("attachments/test.pdf", attachment.Content);
+        }
+    }
+
+    [Test]
+    [Order(203)]
+    public void GetStatus_ForAllPendingSubmissions() {
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+        submissions.Count().Should().BeGreaterThan(0);
+
+        foreach (var submission in submissions) {
+            subscriber.GetStatusForSubmission(submission.CaseId).ForEach(s =>
+                Logger.LogInformation("{SubmissionCaseId} - {ObjEventTime} - {ObjEventType}",
+                    submission.CaseId, s.EventTime, s.EventType));
+        }
+    }
+
+    //[Ignore("Takes a while")]
+    [Test]
+    [Order(204)]
+    public void GetAttachment_FromAllPendingSubmission_ShouldReturnAttachment() {
+        // Arrange
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+        submissions.Count().Should().BeGreaterThan(0);
+
+        foreach (var submission in submissions) {
+            Console.WriteLine(
+                $"Getting submission {submission.SubmissionId} - case {submission.CaseId}");
+            var submissionId = submission.SubmissionId!;
+            if (!Directory.Exists($"./attachments/{submissionId}/"))
+                Directory.CreateDirectory($"./attachments/{submissionId}/");
+
+            subscriber.GetStatusForSubmission(submission.CaseId).ForEach(s =>
+                Console.WriteLine($"{s.EventTime} - {s.EventType}"));
+
+            var subscriberWithSubmission = subscriber
+                .RequestSubmission(submissionId, true);
+            var attachments = subscriberWithSubmission
+                .GetAttachments();
+            foreach (var attachment in attachments)
+                if ((attachment?.Content?.Length ?? 0) > 0)
+                    File.WriteAllBytes(
+                        Path.Combine($"./attachments/{submissionId}/",
+                            attachment.Filename),
+                        attachment.Content);
+
+            Console.WriteLine($"Json Fachdaten: \r\n{subscriberWithSubmission.GetDataJson()}");
+            Console.WriteLine($"Success {submissionId}");
+        }
+
+
+        // Process.Start("open", "./attachments");
+    }
+
+    [Ignore("ID is already been used")]
+    [TestCase("b0bd846f-f56a-4d06-a586-14fb0ea94fd0")]
+    public void GetSingleSubmission_WithSubmissionId_ShouldReturnSubmissionsForPickupDto(
+        string submissionId) {
+        // Arrange
+        var submissions = subscriber.GetAvailableSubmissions().ToList();
+        submissions.Count().Should().BeGreaterThan(0);
+
+        Console.WriteLine($"Getting submission {submissionId}");
+        var dto = subscriber.RequestSubmission(submissionId);
+
+        // Assert
+        dto.Submission.Id.Should().Be(submissionId);
+
+
+        Console.WriteLine(dto.Submission.Data);
+        Console.WriteLine(JsonConvert.SerializeObject(dto.Submission.Metadata));
+    }
+}
diff --git a/IntegrationTests/Test.pdf b/IntegrationTests/Test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..50a038977b732f8c86889b164166410d5d60e0de
Binary files /dev/null and b/IntegrationTests/Test.pdf differ
diff --git a/IntegrationTests/http-client.env.json b/IntegrationTests/http-client.env.json
new file mode 100644
index 0000000000000000000000000000000000000000..a7edad8821b9c1673e132ac9b6e88fc511e6f142
--- /dev/null
+++ b/IntegrationTests/http-client.env.json
@@ -0,0 +1,8 @@
+{
+  "sender": {
+    "baseurl": "https://auth-testing.fit-connect.fitko.dev",
+    "id": "73a8ff88-076b-4263-9a80-8ebadac97b0d",
+    "secret": "rdlXms-4ikO47AbTmmCTdzFoE4cTSt13JmSbcY5Dhsw",
+    "scope": "send:region:DE"
+  }
+}
\ No newline at end of file
diff --git a/IntegrationTests/token.http b/IntegrationTests/token.http
new file mode 100644
index 0000000000000000000000000000000000000000..ecfa23a547330937de73b59e17b59559f507cfa6
--- /dev/null
+++ b/IntegrationTests/token.http
@@ -0,0 +1,14 @@
+### Getting the data from the database BEARER
+POST {{baseurl}}/token
+Content-Type: application/x-www-form-urlencoded
+Accept: application/json
+
+grant_type=client_credentials&client_id={{id}}&client_secret={{secret}}
+
+### Getting the data from the database BEARER
+POST {{baseurl}}/token
+Content-Type: application/x-www-form-urlencoded
+Accept: application/json
+
+grant_type=client_credentials&client_id=WRONG&client_secret=WRONG
+
diff --git a/MockContainer/MockContainer.cs b/MockContainer/MockContainer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2bfa17bde88b8a27fb6ef0f67c70d0a278edcc30
--- /dev/null
+++ b/MockContainer/MockContainer.cs
@@ -0,0 +1,181 @@
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Autofac;
+using FitConnect.Encryption;
+using FitConnect.Models;
+using FitConnect.Models.Api.Metadata;
+using FitConnect.Services.Interfaces;
+using FitConnect.Services.Models;
+using FitConnect.Services.Models.v1.Destination;
+using FitConnect.Services.Models.v1.Submission;
+using Microsoft.Extensions.Logging;
+using Moq;
+using Newtonsoft.Json;
+using Data = FitConnect.Models.Data;
+using JsonSerializer = System.Text.Json.JsonSerializer;
+using Metadata = FitConnect.Models.Api.Metadata.Metadata;
+using Route = FitConnect.Services.Models.v1.Routes.Route;
+
+namespace MockContainer;
+
+public record MockSettings(string PrivateKeyDecryption, string PrivateKeySigning,
+    string PublicKeyEncryption, string PublicKeySignatureVerification, string SenderClientId,
+    string SenderClientSecret, string SubscriberClientId, string SubscriberClientSecret,
+    string DestinationId, string LeikaKey);
+
+public class TestFile {
+    public byte[] Content;
+    public string Name;
+
+    public TestFile() {
+        Content = File.ReadAllBytes("Test.pdf");
+        Name = "Test.pdf";
+    }
+}
+
+public static class Container {
+    public static IContainer Create() {
+        var builder = new ContainerBuilder();
+
+        builder.Register(c => new TestFile()).As<TestFile>();
+        builder.Register(c => CreateOAuthService().Object).As<IOAuthService>();
+        builder.Register(c => CreateRouteService().Object).As<IRouteService>();
+        builder.Register(c => CreateSubmissionService().Object).As<ISubmissionService>();
+        builder.Register(c => CreateDestinationService().Object).As<IDestinationService>();
+        builder.Register(c => Mock.Of<ICasesService>()).As<ICasesService>();
+        builder.Register(c => LoggerFactory.Create(
+                b => {
+                    b.AddSimpleConsole();
+                    b.SetMinimumLevel(LogLevel.Information);
+                }).CreateLogger("FluentSenderTests")
+        ).As<ILogger>();
+
+        CreateEncryptionSettings(builder);
+
+        return builder.Build();
+    }
+
+    private static Mock<IDestinationService> CreateDestinationService() {
+        var mock = new Mock<IDestinationService>();
+        mock.Setup(x => x.GetPublicKey(It.IsAny<string>())).Returns(() =>
+            File.ReadAllTextAsync("./encryptionKeys/publicKey_encryption.json"));
+        return mock;
+    }
+
+    private static void CreateEncryptionSettings(ContainerBuilder builder) {
+        var privateKeyDecryption = File.ReadAllText("./encryptionKeys/privateKey_decryption.json");
+        var privateKeySigning = File.ReadAllText("./encryptionKeys/privateKey_signing.json");
+        var publicKeyEncryption = File.ReadAllText("./encryptionKeys/publicKey_encryption.json");
+        var publicKeySignature =
+            File.ReadAllText("./encryptionKeys/publicKey_signature_verification.json");
+
+        var credentials =
+            JsonConvert.DeserializeObject<dynamic>(
+                File.ReadAllText("./encryptionKeys/credentials.json"));
+
+        var senderClientId = (string)credentials.sender.clientId;
+        var senderClientSecret = (string)credentials.sender.clientSecret;
+        var subscriberClientId = (string)credentials.subscriber.clientId;
+        var subscriberClientSecret = (string)credentials.subscriber.clientSecret;
+        var destinationId = (string)credentials.destinationId;
+        var leikaKey = (string)credentials.leikaKey;
+
+        builder.Register(c => new MockSettings(
+                privateKeyDecryption, privateKeySigning,
+                publicKeyEncryption, publicKeySignature,
+                senderClientId, senderClientSecret,
+                subscriberClientId, subscriberClientSecret,
+                destinationId, leikaKey))
+            .As<MockSettings>();
+        builder.Register(c => new KeySet {
+            PrivateKeyDecryption = privateKeyDecryption,
+            PrivateKeySigning = privateKeySigning,
+            PublicKeyEncryption = publicKeyEncryption,
+            PublicKeySignatureVerification = publicKeySignature
+        }).As<KeySet>();
+    }
+
+    private static Mock<IRouteService> CreateRouteService() {
+        var routeService = new Mock<IRouteService>();
+        routeService.Setup(r => r.GetDestinationIdAsync(
+                It.Is<string>(s => !string.IsNullOrWhiteSpace(s)),
+                It.IsAny<string>(),
+                It.IsAny<string>(),
+                It.IsAny<string?>()))
+            .Returns(() => Task.Run(() => new List<Route>()));
+        return routeService;
+    }
+
+    private static Mock<IOAuthService> CreateOAuthService() {
+        var oAuthService = new Mock<IOAuthService>();
+        oAuthService.SetupGet(o => o.IsAuthenticated).Returns(true);
+        return oAuthService;
+    }
+
+    private static Mock<ISubmissionService> CreateSubmissionService() {
+        var submissionService = new Mock<ISubmissionService>();
+        submissionService.Setup(s =>
+            s.ListSubmissions(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>())).Returns(
+            () =>
+                Task.Run(() => JsonConvert.DeserializeObject<SubmissionsForPickupDto>(
+                    @"{""offset"":0,""count"":3,""totalCount"":3,""submissions"":[{""destinationId"":""879ee109-a690-4db8-ab32-424284184d7d"",""submissionId"":""ce75a6b8-d72f-4b94-b09e-af6be35bc2ae""},{""destinationId"":""19c8489b-29b8-422f-b7db-919852cfb04b"",""submissionId"":""e364430f-5a3b-4284-ba9a-f2867ba421e6""},{""destinationId"":""80a0aac3-148d-42bb-9366-516ce6355348"",""submissionId"":""530ba588-2db9-4899-ab0d-0c0b57689271""}]}"))
+        );
+        submissionService.Setup(s => s.CreateSubmission(It.IsAny<CreateSubmissionDto>())).Returns(
+            () => new SubmissionCreatedDto {
+                DestinationId = "destinationId",
+                CaseId = "caseId"
+            });
+
+        submissionService.Setup(s => s.GetKey(It.IsAny<string>())).Returns(() =>
+            "{\"kty\":\"RSA\",\"key_ops\":[\"wrapKey\"],\"alg\":\"RSA-OAEP-256\",\"x5c\":[\"MIIFCTCCAvECBAONrDowDQYJKoZIhvcNAQENBQAwSTELMAkGA1UEBhMCREUxFTATBgNVBAoMDFRlc3RiZWhvZXJkZTEjMCEGA1UEAwwaRklUIENvbm5lY3QgVGVzdHplcnRpZmlrYXQwHhcNMjIwNjI4MTE1MzE1WhcNMzIwNjI1MTE1MzE1WjBJMQswCQYDVQQGEwJERTEVMBMGA1UECgwMVGVzdGJlaG9lcmRlMSMwIQYDVQQDDBpGSVQgQ29ubmVjdCBUZXN0emVydGlmaWthdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM4xtyHNdvpBlol6lROwg4yAsgG9zKQ7oQkjNTUxnbsuiqhRPpniY1/ZbtTEZqrsDLtSzb9B48eJLIgdGS+mI5Z6kFTG1XaMZ46XEJxZNY/Ldf53YTmrW3QqxGV05t7/ylqWQZtSCPDsHjfX5SyoJOFTPWMqFjLiKllJzBi2j/FTbJ73K1gVUv7V7UiRuARfqScDUH6egNKza7CpvzARft7kyF3za3cm4w1rtV6yBkduWOq/JLzctMsp7vbX4S8AW6m4BvD2JLSMukx7KXY2u0AgtmnzNCDXItRo/+PGH3rC3C27/oyIyykJ94fZsSiz2FffnbEUgmU+x6RzwWfT3KorgG3wI6cECiBmFCZLdBhjoZzPpYyr6LBrIeMbAip3CWxNnayB2zNZIhlrhkPw1WkxcZDBLJaql/P7dQKknU1g94KiPtNjxnO8NSfolFkLGQ3zT0LIoSXYPyHMOiG8xgKczSMgBXkkoCTTSUqWOXlx2QeFdCKBkxxW947fMKpA8nGIhIk4JiycpV1Xf5eQ5alz+Ox3QeUSi22mSN4hhDMyvfLwZeKlpIsQwcQ8zjWuXllHJAdfr3WGLj5LCk6i5NVpYfWtm++SrDiSnGgTlxB7n1SAWcvGvhhm5INta5xgmFOY5i3DwYo9A9NzxZw+7tEZT2k3uY1gmxd6GDJkOhIXAgMBAAEwDQYJKoZIhvcNAQENBQADggIBAI2knmZldxsrJp6Lyk+eLEVbrZI3BuHdfs+9q5I8R2b/RoqEuI05ZxoFPMZGyOMUINsXpthWMGsyALs512nmzR4bD1NGu5Ada3P/UiK3zaZk+xpJ9qAJo9xATRlGkdPhW5HyztUap1M+UW0GA/fDZJLRDVAlF+8czCPahP8ZhP5pkZcPTL/xxvQwDmadmdDRAEObOQlx58SwqQYC/FnO6lEFRhY+Hak9W4E1MoegoG8KFwOvqVRiRx3IVy1vdMQZgRRLDLkZzKIlI8WwkJONObVqdSrF2HfnEk6jhsG7/4Prn16XBSRi7wdqLOnnUpxwKsvL7BZVqAPcJ821XLxZ8wmRVItMnO3qJPP6RqVj7wfB7hnUOLHDywbGGWSrk0gi3x81x3tYXf8S+AbEn59uGIiArZjKmErvgFdtCWS8ILd7EFGYaMSYCCaJxoM/N5LckxAQXmsCgiLeOxXez2+H/uTRbctc5XEgNuVt+k92bF8amaGi5gUO60v7k4hPsnSHFzIqhsfiiOE6Pewyt96teAx4Xc2vMULcc2MUOHeiqK77OzHj8jN+/nztVSnYSrbaPYhJuIXVW5UZExG8kCTSNKK3aS+4++El7sbIHsqBTKvTmQvOMaBrGbZZR5jy5RTZSPi4njBdkjzRogloZRBoOSLebbR4B+4LMeoidxoOQN6O\"],\"kid\":\"vr7cWKGl-g4Wa_CRGowNhAYW_gQb-akMbiigxN0EkDI\",\"n\":\"zjG3Ic12-kGWiXqVE7CDjICyAb3MpDuhCSM1NTGduy6KqFE-meJjX9lu1MRmquwMu1LNv0Hjx4ksiB0ZL6YjlnqQVMbVdoxnjpcQnFk1j8t1_ndhOatbdCrEZXTm3v_KWpZBm1II8OweN9flLKgk4VM9YyoWMuIqWUnMGLaP8VNsnvcrWBVS_tXtSJG4BF-pJwNQfp6A0rNrsKm_MBF-3uTIXfNrdybjDWu1XrIGR25Y6r8kvNy0yynu9tfhLwBbqbgG8PYktIy6THspdja7QCC2afM0INci1Gj_48YfesLcLbv-jIjLKQn3h9mxKLPYV9-dsRSCZT7HpHPBZ9PcqiuAbfAjpwQKIGYUJkt0GGOhnM-ljKvosGsh4xsCKncJbE2drIHbM1kiGWuGQ_DVaTFxkMEslqqX8_t1AqSdTWD3gqI-02PGc7w1J-iUWQsZDfNPQsihJdg_Icw6IbzGApzNIyAFeSSgJNNJSpY5eXHZB4V0IoGTHFb3jt8wqkDycYiEiTgmLJylXVd_l5DlqXP47HdB5RKLbaZI3iGEMzK98vBl4qWkixDBxDzONa5eWUckB1-vdYYuPksKTqLk1Wlh9a2b75KsOJKcaBOXEHufVIBZy8a-GGbkg21rnGCYU5jmLcPBij0D03PFnD7u0RlPaTe5jWCbF3oYMmQ6Ehc\",\"e\":\"AQAB\",\"use\":null}");
+
+        submissionService
+            .Setup(c => c.SubmitSubmission(It.IsAny<string>(), It.IsAny<SubmitSubmissionDto>()))
+            .Returns(
+                (string id, SubmitSubmissionDto dto) => {
+                    Console.WriteLine(
+                        $@"Submitting submission {id} with 
+                            {JsonSerializer.Serialize(dto, new JsonSerializerOptions {
+                                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+                                WriteIndented = true,
+                                Encoder = JavaScriptEncoder.Default
+                            })}");
+                    return Task.Run(() => new SubmissionReducedDto());
+                });
+
+        var data = new Data();
+
+        var metadata = new Metadata {
+            PublicServiceType = new Verwaltungsleistung {
+                Name = "Verwaltungsleistung"
+            }
+        };
+
+
+        var encryptData = "";
+
+        var encryptMetadata = "";
+
+        submissionService.Setup(s => s.GetSubmission(It.IsAny<string>()))
+            .Returns((string id) =>
+                new SubmissionDto {
+                    SubmissionId = id,
+                    DestinationId = "destination",
+                    Callback = new CallbackDto {
+                        Secret = "secret",
+                        Url = "http://localhost:8080/callback"
+                    },
+                    EncryptedData = encryptData,
+                    EncryptedMetadata = encryptMetadata,
+                    Attachments = new List<string>(),
+                    CaseId = "caseId",
+                    ServiceType = new ServiceTypeDto {
+                        Description = "ServiceType",
+                        Identifier = "Service identifier",
+                        Name = "Dummy Service Type"
+                    }
+                });
+        return submissionService;
+    }
+}
diff --git a/MockContainer/MockContainer.csproj b/MockContainer/MockContainer.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..aa996437736a92a58456a8e7d0d74f0a15a938c7
--- /dev/null
+++ b/MockContainer/MockContainer.csproj
@@ -0,0 +1,45 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+        <IsPackable>false</IsPackable>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Autofac" Version="6.4.0" />
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
+        <PackageReference Include="Moq" Version="4.18.1" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\FitConnect\FitConnect.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <None Update="encryptionKeys\privateKey_decryption.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\privateKey_signing.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\publicKey_encryption.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\publicKey_signature_verification.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\set-public-keys.json">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="Test.pdf">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+        <None Update="encryptionKeys\credentials.json">
+          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+
+</Project>
diff --git a/MockContainer/Test.pdf b/MockContainer/Test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..50a038977b732f8c86889b164166410d5d60e0de
Binary files /dev/null and b/MockContainer/Test.pdf differ
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000000000000000000000000000000000000..1a588142cbbc9dcf8188bb279c128e462a77aedf
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,3 @@
+# Changelog
+
+## [Unreleased]
diff --git a/readme.md b/readme.md
index 901c707ae22453f6b40f4c80d75c515cd9f48a1d..4a04ed5766510d6e3887d3bbd932c5434c72567a 100644
--- a/readme.md
+++ b/readme.md
@@ -1,28 +1,204 @@
 # Fit-Connect .NET SDK
 
+For an implementation example take a look at the [DemoRunner](DemoRunner/Program.cs)
+
+## **!! IN DEVELOPMENT NOT FOR PRODUCTION USE !!**
+
 **Fit-Connect .NET SDK** is a .NET library for the Fit-Connect API.
 
-**IN DEVELOPMENT NOT FOR PRODUCTION USE**
+Die Verwendung des SDKs ist in der [documentation (ger)](./Documentation/documentation.de-DE.md)
+erklärt
+
+## For information how to use the SDK and FitConnect visit:
+
+* [SDK-Dokumentation](./documentation/documentation.de-DE.md)
+* [FIT-Connect Dokumentation](https://docs.fitko.de/fit-connect/docs)
+* [FIT-Connect Dokumentation](https://fit-connect.com/docs)
+
+<!-- 
+## Structure
+For the structure look the [Structure documentation](structure.md) 
+-->
+
+# Allgemeines
+
+## Environment
+
+Das FitConnect SDK kann an die folgenden Umgebungen angeschlossen werden:
 
-For an implementation example take a look at the [DummyClient](DummyClient/Program.cs)
+- Testing: ```FitConnectEnvironment.Testing```
+- Staging: ```FitConnectEnvironment.Staging```
+- Production: ```FitConnectEnvironment.Production```
 
-## Ignored Files
 
-You need a secret file for e2e test like:
+[FIT Connect Umgebungen](https://docs.fitko.de/fit-connect/docs/getting-started/first-steps/#environments)
 
-```json
-{
-  "sender": {
-    "id": "00000000-0000-0000-0000-000000000000",
-    "secret": "0000000000000000000000000000000000000000000",
-    "scope": "send:region:DE"
-  }
-}
+Hierfür muss der Client die Environment auswählen oder einen eigenen Environment-Parameter übergeben.
+
+## Credentials
+
+ClientId und ClientSecret sind die Grundlage, um einen Token vom OAuth-Server abfragen zu können.
+Die beiden Werte sind im [Self-Service Portal der Testumgebung von FIT-Connect](https://portal.auth-testing.fit-connect.fitko.dev/clients) zu erstellen.
+
+# Sender
+
+```mermaid
+flowchart LR
+    start([GetSender])
+    destination(WithDestination)
+    service(WithServiceType)
+    attachments(WithAttachments)
+    data([WithData])
+    
+    start-->destination-->service-->attachments-->data
 ```
 
-[glossary](https://docs.fitko.de/fit-connect/docs/glossary/)
+Das SDK verhindert auf Grund der FluentAPI, dass die Methoden in der falschen Reihe aufgerufen werden können.
+
+## GetSender(FitConnectEnvironment.Testing, clientId, clientSecret, logger)
+
+Hier werden die FIT Connect Environment ausgewählt und die Credentials übergeben.
+Der Parameter ```logger``` ist optional und muss das Interface ```Microsoft.Extensions.Logging.ILogger``` implementieren.
+
+## .WithDestination(destinationId)
+
+Die Destination ID des Zustelldienstes muss an dieser Stelle übergeben werden.
+
+_Noch nicht vollständig getestet!_<br>
+Eine Möglichkeit, die Destination ID zu ermitteln, geht über die Methode ``FindDestinationId()`` des Senders.
+
+## .WithServiceType("FIT Connect Demo", leikaKey)
+
+Der Service Type muss an dieser Stelle übergeben werden.
+Hierfür wird ein Leistungsschlüssel (LeiKa-Schlüssel) benötigt.
+Leistungsschlüssel haben diese Form ```urn:de:fim:leika:leistung:99400048079000```
+
+## .WithAttachments(new Attachment("Test.pdf", "Test Attachment"))
+
+Die Anhänge zum Antrag werden mit ```.WithAttachments``` übergeben.
+Diese Methode erwartet ein Array von Attachment Objekten die als ```params``` übergeben werden können.
+
+Das Attachment kann mit den folgenden Parametern erstellt werden:
+
+- Metadaten und byte[] content
+- Dateiname und Beschreibung
+
+Dazu werden zwei Konstruktoren bereitgestellt:
+
+- ```Attachment(Api.Metadata.Attachment metadata, byte[] content)```
+- ```Attachment(string fileName, string description,)```
+
+## .WithData("{\"message\":\"Hello World\"}")
+
+Die Fachdaten werden als JSON String übergeben.
+
+## .Submit()
+
+Das Abschicken der Submission erfolgt mit diesem Aufruf.
 
-### Tickets
+## Beispiel
 
-- [Method signatures](https://git.fitko.de/fit-connect/planning/-/issues/438)
-- [Java SDK](https://git.fitko.de/fit-connect/planning/-/issues/413)
\ No newline at end of file
+```csharp
+var submission = Client
+    .GetSender(FitConnectEnvironment.Development, clientId, clientSecret, logger)
+    .WithDestination(destinationId)
+    .WithServiceType("FIT Connect Demo", leikaKey)
+    .WithAttachments(new Attachment("Test.pdf", "Test Attachment"))
+    .WithData("{\"message\":\"Hello World\"}")
+    .Submit();
+```
+
+# Subscriber
+
+Der Subscriber braucht zusätzliche Informationen, um die Submissions abrufen zu können.
+Hier sind zusätzlich die Schlüssel zum Ver- und Entschlüsseln notwendig.
+
+````mermaid
+flowchart LR
+
+    start([GetSubscriber])
+    availableSub(GetAvailableSubmission)
+    requestSub(RequestSubmission)
+    subscriberWithSub([SubscriberWithSubmission])
+    subscriberWithSub_([SubscriberWithSubmission])
+    data(GetDataJson)
+    attachments(GetAttachments)
+    accept(AcceptSubmission)
+    reject(RejectSubmission)
+    forward(ForwardSumbission)
+    finished{{Abgeschlossen}}
+    rejected{{Zurueckgewiesen}}
+    
+    start-->availableSub-->requestSub-->subscriberWithSub
+    
+    subscriberWithSub_-->data-->attachments
+    attachments-->accept-->finished
+    attachments-->reject-->rejected
+    attachments-->forward
+
+        
+````
+
+## .GetSubscriber(...)
+
+Hier werden die FIT Connect Environment ausgewählt, die Keys und die Credentials übergeben.
+Der Parameter ```logger``` ist optional und muss das Interface ```Microsoft.Extensions.Logging.ILogger``` implementieren.
+
+## .GetAvailableSubmissions()
+
+Liefert eine Liste mit den verfügbaren Submissions zurück.
+
+## .RequestSubmission(submissionId)
+
+Hiermit wird die Submission abgerufen und im Subscriber gespeichert.
+Der Rückgabewert der Funktion ist also _Subscriber mit einer Submission_
+
+## .GetDataJson()
+
+Liefert die Fachdaten als JSON String zurück.
+
+## .GetAttachments()
+
+Gibt eine Liste mit den Attachments der Submission zurück.
+Die Attachments können so geprüft werden.
+
+## .AcceptSubmission()
+
+Akzepiert die Submission und löscht diese vom Server.
+
+## .RejectSubmission()
+
+Weißt die Submission zurück.
+
+## .ForwardSubmission()
+
+## Beispiel
+
+```csharp
+        var subscriber = Client.GetSubscriber(FitConnectEnvironment.Testing, clientId,
+            clientSecret,
+            privateKeyDecryption,
+            privateKeySigning,
+            publicKeyEncryption,
+            publicKeySignatureVerification,
+            logger);
+
+        var submissions = subscriber.GetAvailableSubmissions();
+
+        // Alle verfügbaren Submissions laden
+        foreach (var submission in submissions) {
+            var subscriberWithSubmission = subscriber.RequestSubmission(submission.SubmissionId);
+            
+            // Laden der Anhänge
+            var attachments = subscriberWithSubmission
+                .GetAttachments();
+
+            // Ausgabe der Fachdaten auf der Konsole
+            logger.LogInformation("Fachdaten: {Data}", subscriberWithSubmission.GetDataJson());
+            
+            // Submission akzeptieren und abschließen
+            subscriberWithSubmission.AcceptSubmission();
+        }
+```
+
+[glossary](https://docs.fitko.de/fit-connect/docs/glossary/)
diff --git a/structure.md b/structure.md
new file mode 100644
index 0000000000000000000000000000000000000000..7449047f09344202257861f9c18b288de0a57562
--- /dev/null
+++ b/structure.md
@@ -0,0 +1,126 @@
+# Structure
+
+## Functional Diagram
+
+```mermaid
+ classDiagram
+  class FitConnectClient{
+   FitConnectApiService
+  }
+  
+  class FitConnectApiService{
+    CasesService
+    DestinationService
+    InfoService
+    OAuthService  
+    RouteService  
+  }
+  
+  FitConnectClient ..> SubmissionSender : public
+  FitConnectClient ..> SubmissionSubscriber : public
+  SubmissionSender --|> FunctionalBaseClass
+  SubmissionSubscriber --|> FunctionalBaseClass
+  FunctionalBaseClass ..> FitConnectApiService : protected
+
+  FitConnectApiService ..> RouteService : public
+  FitConnectApiService ..> CasesService : public
+  FitConnectApiService ..> DestinationService : public
+  FitConnectApiService ..> InfoService : public
+  FitConnectApiService ..> OAuthService : public
+  
+  RouteService --|> RestCallService : Inheritance
+  CasesService --|> RestCallService : Inheritance
+  DestinationService --|> RestCallService : Inheritance
+  InfoService --|> RestCallService : Inheritance
+  OAuthService --|> RestCallService : Inheritance
+  
+```
+
+With that structure the user of the SDK is intended to see the following: (For easier reading all
+methods are shown sync)
+
+```mermaid
+  classDiagram
+    class FitConnectClient {
+    << Draft >>
+        - Sender
+        - Subscriber
+        + SendSumission(submission)
+        + GetSubmissions(offset, limit)
+        + GetSubmission(id)
+    }
+    
+    class Sender {
+    << Draft >>
+      + bool CheckPublicKey(string publicKey)
+      + bool CheckPublicKey(byte[] publicKey)
+      + string EncryptData(string? data)
+      + string EncryptAttachment(byte[] attachment)
+      + string CreateMetadata(string data, byte[] attachment) ???
+      
+      * Pruefung von oeffentlichen Schluesseln und Zertifikatsketten + OCSP-Check 
+      * Verschluesselung von Fachdaten [JSON, XML] mittels JWE
+      * Verschluesselung von Anhängen [Binaerdaten] mittels JWE
+      * Korrekte Erzeugung eines Metadatensatzes inkl. Hashwerte
+   }
+    
+    class Subscriber { 
+    << Draft >>       
+       + byte[] DecryptAttachment(string attachment)
+       + SecurityEventToken CreateSecurityEventToken(string data, string attachment, string privateKey)
+       + bool CheckMetadata(string metaData)
+       + string DecryptData(string data)
+       + bool CheckHash(string metaData, string hash)
+       
+       * Entschluesselung von Fachdaten [JSON oder XML] mittels JWE
+       * Entschluesselung von Anhaengen [Binaerdaten] mittels JWE
+       * Pruefung der empfangenen Metadaten gegen das zugehörige JSON-Schema
+       * Pruefung der Hashwerte aus dem Metadatensatz.
+       * SET-Erstellung inkl. Signaturerzeugung
+
+    }
+    
+    class FunctionalBaseClass{
+       + GetOAuthTokenAsync(string clientId, string clientSecret, string? scope)
+       + SecurityEventToken GetSetData()
+       * SET-Empfang inkl. Signaturpruefung
+       * Unterstuezung / Abstraktion der API-Nutzung
+       * Abruf des OAuth-Tokens
+    }
+    
+    FitConnectClient <--> Sender
+    FitConnectClient <--> Subscriber
+    
+    Sender --|> FunctionalBaseClass
+    Subscriber --|> FunctionalBaseClass
+```
+
+## Class diagram
+
+```mermaid
+ classDiagram
+    class Client{
+    }
+```
+
+## Annotation
+
+The structure ensures an abstraction of the API calls for the user and a 'simple' way to change the
+API calls if a new version of the API is released. Currently the API call is not versioned and hard
+coded.
+
+### Abstractions for API calls
+
+- The use of a dependency injection can be added to
+  inject the API calls into the SDK. But is not yet implemented.
+- Another approach would be using a generic class for the API calls.
+
+## Implementation
+
+### Sender
+
+#### WithDestination(string destinationId)
+
+1. Call server to check if the destination is valid.
+2. Get public key for the destination and store the public key in the submission.
+3. Create the submission to get the submissionId
diff --git a/test.Dockerfile b/test.Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..7c37cb3a64135354d516de3366e209896dfc16a8
--- /dev/null
+++ b/test.Dockerfile
@@ -0,0 +1,6 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0
+WORKDIR /test
+COPY . .
+RUN dotnet restore
+RUN dotnet build
+RUN dotnet test --filter E2ETests.SenderTest
diff --git a/version.sh b/version.sh
index 45424906652f8cb4330861392b7062a224e8295a..e3da4df0add6637e02224dad4127ccc1d30be6b2 100755
--- a/version.sh
+++ b/version.sh
@@ -1,34 +1,59 @@
 #!/bin/zsh
 
+### Setup OpenSSL
+if [ "$(uname)" = "Darwin" ]; then
+  echo "macOS"
+  echo "Trying to import openssl"
+  OPENSSL_PATH=/usr/local/opt/openssl@1.1/lib
+  if test -d "$OPENSSL_PATH"; then
+    export DYLD_LIBRARY_PATH=$OPENSSL_PATH
+  else
+    echo "OpenSSL not found"
+    exit 100
+  fi
+elif [ "$(uname)" = "Linux" ]; then
+  echo "Linux"
+else
+  echo "Unknown"
+fi
+
+### Running build script
 if [ -z "$1" ]; then
   echo "Usage: $0 <version>"
   exit 1
 fi
 
-sed -i "" -e "s|<AssemblyVersion>.*</AssemblyVersion>|<AssemblyVersion>$1</AssemblyVersion>|" FitConnect/FitConnect.csproj
-sed -i "" -e "s|<FileVersion>.*</FileVersion>|<FileVersion>$1</FileVersion>|" FitConnect/FitConnect.csproj
+FILE_VERSION=$($1 | sed -r 's|^(([0-9]+).([0-9]+).([0-9]+))(.*)$|\1|')
+
+sed -i "" -e "s|<AssemblyVersion>.*</AssemblyVersion>|<AssemblyVersion>$FILE_VERSION</AssemblyVersion>|" FitConnect/FitConnect.csproj
+sed -i "" -e "s|<FileVersion>.*</FileVersion>|<FileVersion>$FILE_VERSION</FileVersion>|" FitConnect/FitConnect.csproj
 sed -i "" -e "s|<PackageVersion>.*</PackageVersion>|<PackageVersion>$1</PackageVersion>|" FitConnect/FitConnect.csproj
 
+dotnet clean
 
-if ! dotnet test; then
-  echo "Test failed"
-  exit 1
-fi
+#if ! dotnet test -c Release; then
+#  echo "Test failed"
+#  exit 1
+#fi
 
-if ! dotnet build; then
+if ! dotnet build FitConnect -c Release; then
   echo "Build failed"
   exit 1
 fi
 
-if ! dotnet pack; then
+if ! dotnet pack -c Release; then
   echo "Pack failed"
   exit 1
 fi
 
-CURRENT=$(git branch | grep \* | cut -d ' ' -f2)
-if [ "$CURRENT" != "master" ]; then
-  echo "Not on master branch"
-  exit 1
-fi
+cp "FitConnect/bin/Release/FitConnect.$1.nupkg" .
+
+exit 0
+
+#CURRENT=$(git branch | grep \* | cut -d ' ' -f2)
+#if [ "$CURRENT" != "master" ]; then
+#  echo "Not on master branch"
+#  exit 1
+#fi
 
-# Here you can do the release things...
\ No newline at end of file
+# Here you can do the release things...
diff --git a/working_notes.md b/working_notes.md
index 517d433b53dbfa873b286a70452895f17e60a3eb..37494dfd9b86ca9b98063b8c25ba0b35243dc2da 100644
--- a/working_notes.md
+++ b/working_notes.md
@@ -2,21 +2,7 @@
 
 # TODOS
 
-| interface | implemented | tested | scope              | description                                                                                                                                   |
-|:---------:|:-----------:|:------:|:-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
-|     X     |      X      |   X    | Sender,Subscriber  | Abruf von OAuth-Tokens                                                                                                                        |
-|     X     |             |        | Sender             | Prüfung von öffentlichen Schlüsseln und Zertifikatsketten + OCSP-Check (vgl. [#119](https://git.fitko.de/fit-connect/planning/-/issues/119))  |
-|     X     |             |        | Sender             | Verschlüsselung von Fachdaten (JSON, XML) mittels JWE                                                                                         |
-|     X     |             |        | Sender             | Verschlüsselung von Anhängen (Binärdaten) mittels JWE                                                                                         |
-|     X     |             |        | Sender             | Korrekte Erzeugung eines Metadatensatzes inkl. [Hashwerte](https://docs.fitko.de/fit-connect/docs/sending/metadata#integrity)                 |
-|     X     |             |        | Subscriber         | Entschlüsselung von Fachdaten (JSON oder XML) mittels JWE                                                                                     |
-|     X     |             |        | Subscriber         | Entschlüsselung von Anhängen (Binärdaten) mittels JWE                                                                                         |
-|     X     |      X      |        | Subscriber         | Prüfung der empfangenen Metadaten gegen das zugehörige JSON-Schema                                                                            |
-|     X     |             |        | Subscriber         | [Prüfung der Hashwerte](https://docs.fitko.de/fit-connect/docs/receiving/verification#integrity) aus dem Metadatensatz.                       |
-|     X     |             |        | Subscriber         | SET-Erstellung inkl. Signaturerzeugung                                                                                                        |
-|     X     |             |        | Sender, Subscriber | SET-Empfang inkl. Signaturprüfung                                                                                                             |
-|     X     |             |        | Sender, Subscriber | Unterstüzung / Abstraktion der API-Nutzung (`fitconnect.sendSubmission(metadata, destinationID, ...)` o.ä.) für die oben genannten Use-Cases  |
-|     X     |             |        | Sender, Subscriber | Logging (Logging-Modul muss von außen kommen)                                                                                                 |
+Ticket: [Issue 440](https://git.fitko.de/fit-connect/planning/-/issues/440)
 
 ## Links