diff --git a/README.md b/README.md
index 7e248357f259684e0f1c3a8911d313582c5d6434..ab90980c2c64c5fe4280d6f5fc1fe09961758aa3 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,7 @@ See the projects' module documentation for more specific information:
 * [API Module ](api/README.md)
 * [Core Module ](core/README.md)
 * [Client Module ](client/README.md)
+* [Integration-Test Module ](integration-tests/README.md)
 
 ## Setup
 
@@ -110,20 +111,20 @@ _The following steps show how to get the SDK running_
     ````java
       final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(Path.of("path/to/config.yml"));
       ````
-7. Afterwards the config is used to initialize clients with the  `ClientFactory` that offer clients for sender, subscriber and routing:
+7. Afterwards the config is used to initialize clients with the `ClientFactory` that offer clients for sender, subscriber and routing:
     ````java
       final SenderClient senderClient = ClientFactory.getSenderClient(config);
         //
       final SubscriberClient subscriberClient = ClientFactory.getSubscriberClient(config);
         //
-      final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+      final RouterClient routerClient = ClientFactory.getRouterClient(config);
    ````
 
 <p align="right">(<a href="#top">back to top</a>)</p>
 
 ## API Usage for Routing
-The Routing-Client allows to retrieve data about areas and services as well as their service destination.
-A typical workflow using the `RoutingClient` and `SenderClient` would be:
+The Router-Client allows to retrieve data about areas and services as well as their service destination.
+A typical workflow using the `RouterClient` and `SenderClient` would be:
 
 1) Find the `areaId` for an area via routing
 2) Find the destination for a `leikaKey` and an `areaId` via routing
@@ -133,13 +134,13 @@ A typical workflow using the `RoutingClient` and `SenderClient` would be:
 Areas can be searched with one or more search criteria:
 
 ```java
-final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
 final var citySearchCriterion = "Leip*";
 final var zipCodeSearchCriterion = "04229";
 
 // get first 5 area results
-final List<Area> areas = routingClient.findAreas(List.of(citySearchCriterion, zipCodeSearchCriterion), 0, 5);
+final List<Area> areas = routerClient.findAreas(List.of(citySearchCriterion, zipCodeSearchCriterion), 0, 5);
 
 LOGGER.info("Found {} areas", areas.size());
 for (final Area area : areas){
@@ -163,7 +164,7 @@ __Note:__ Both, the `leikaKey` service-identifier and the region keys `ars/ags`c
 #### Find destination by service identifier and *areaId*
 
 ```java
-final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
 final DestinationSearch search = DestinationSearch.Builder()
         .withLeikaKey("99123456760610")
@@ -172,7 +173,7 @@ final DestinationSearch search = DestinationSearch.Builder()
         .build();
 
 // get first 3 route results
-final List<Route> routes = routingClient.findDestinations(search);
+final List<Route> routes = routerClient.findDestinations(search);
 
 LOGGER.info("Found {} routes for service identifier {}", routes.size(), leikaKey);
 for (final Route route : routes){
@@ -185,7 +186,7 @@ for (final Route route : routes){
 Besides the areaId another search criterion for the area/region can be used as well:
 
 ```java
-final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
 final DestinationSearch search = DestinationSearch.Builder()
         .withLeikaKey("99123456760610")
@@ -194,7 +195,7 @@ final DestinationSearch search = DestinationSearch.Builder()
         .build();
 
 // get first 3 route results
-final List<Route> routes = routingClient.findDestinations(search);
+final List<Route> routes = routerClient.findDestinations(search);
 
 LOGGER.info("Found {} routes for service identifier {}", routes.size(), leikaKey);
 for (final Route route : routes){
@@ -202,7 +203,6 @@ for (final Route route : routes){
 }
 ```
 
-
 ## API Usage for Sender
 
 For sending submission and already encrypted submissions builder are provided to construct the necessary payload to be sent. 
@@ -218,8 +218,12 @@ If all data, metadata and attachments are encrypted outside the SDK the sender c
 #### 1.  Retrieve public encryption key:
 
 ```java
+final SenderClient senderClient = ClientFactory.getSenderClient(config);
+
+// destination id that was retrieved via the router client
 final var destinationId = UUID.fromString("d2d43892-9d9c-4630-980a-5af341179b14");
-final String publicJwkAsJsonString =  ClientFactory.getSenderClient(config).getPublicKey(destinationId);
+
+final String publicJwkAsJsonString =  senderClient.getPublicKeyForDestination(destinationId);
 ```
 
 #### 2. Send encrypted data
@@ -469,9 +473,25 @@ if(validationResult.hasError()){
 }
 ```
 
+## Error Handling
+### Exceptions
+All clients accessible via the ``ClientFactory`` throw a custom `RuntimeException` if a technical error occurred:
+- SenderClient => throws ``FitConnectSenderException``
+- SubscriberClient => throws ``FitConnectSubscriberException``
+- RouterClient => throws ``FitConnectRouterException``
+
+For all other issues regarding the configuration and set-up of the SDK a ``FitConnectInitialisationException`` will be thrown.
+
+### Auto-Reject
+The SDK performs validations on all kind of requests e.g. checks for data integrity and JWT-validity. 
+If one of the validations fails on subscriber side, whilst retrieving, the submission it will be auto-rejected. 
+This means a ``REJECT`` event is sent to the event-log with a subsequent deletion of that invalid submission.
+
+Please see the [documentation on verification](https://docs.fitko.de/fit-connect/docs/receiving/verification) for all tests and checks that are executed. 
+
 ## Integration Tests
 
-Integration tests do not run per default with `mvn test`, but they can be executed with the maven profile `IntegrationTests` via `mvn -PIntegrationTests test`. 
+Integration tests do not run per default with `mvn test`, but they can be executed with the maven via `mvn verify`. 
 They expect the following environment variables to be set in the rn configuration of the IDE or on the local terminal:
 
 * SENDER_CLIENT_ID
@@ -491,7 +511,6 @@ var submissionBaseUrl = "https://submission-api-testing.fit-connect.fitko.dev";
 ```
 
 ## Roadmap
-- [ ] Add auto-reject on technical errors
 - [ ] Maven central release of 1.0.0-beta
 
 See the [open issues](https://git.fitko.de/fit-connect/planning/-/boards/44?search=SDK) for a full list of proposed features (and known issues).
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java b/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
index 2226141c85623f070e562b7b7fae1bf4e13cdfb3..cb5b8ff966dae0db75e519abc741f2060a89c474 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/ApplicationConfig.java
@@ -1,18 +1,23 @@
 package dev.fitko.fitconnect.api.config;
 
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
+import dev.fitko.fitconnect.api.config.defaults.ResourcePaths;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
-import lombok.Data;
+import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.With;
+import lombok.experimental.Accessors;
 
 import java.net.URI;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-@Data
+@Getter
 @Builder
+@Accessors(chain = true)
 @NoArgsConstructor
 @AllArgsConstructor
 public class ApplicationConfig {
@@ -29,9 +34,6 @@ public class ApplicationConfig {
     @Builder.Default
     private Integer requestTimeoutInSeconds = 30;
 
-    @Builder.Default
-    private boolean enableAutoReject = true;
-
     @Builder.Default
     private URI setSchemaWriteVersion = SchemaConfig.SET_V_1_0_1.getSchemaUri();
 
@@ -41,31 +43,40 @@ public class ApplicationConfig {
     @Builder.Default
     private URI destinationSchemaVersion = SchemaConfig.XZUFI_DESTINATION_SCHEMA.getSchemaUri();
 
+    private String submissionDataSchemaPath;
+
     private SenderConfig senderConfig;
 
     private SubscriberConfig subscriberConfig;
 
+    @With
     @Builder.Default
-    private Map<EnvironmentName, Environment> environments = Collections.emptyMap();
+    private Map<EnvironmentName, Environment> environments = new HashMap<>();
 
     private EnvironmentName activeEnvironment;
 
-    private String submissionDataSchemaPath;
+    public boolean isAutoRejectEnabled() {
+        return getCurrentEnvironment().getEnableAutoReject();
+    }
 
-    private Environment getEnvironmentByName(final EnvironmentName environmentName) {
-        if (environments.containsKey(environmentName)) {
-            return environments.get(environmentName);
-        } else {
-            throw new InitializationException("No environment with name '" + environmentName.getName() + "' found. Available environments are: " + getAvailableEnvironmentNames());
-        }
+    public boolean isSkipSubmissionDataValidation() {
+        return getCurrentEnvironment().getSkipSubmissionDataValidation();
+    }
+
+    public boolean isAllowInsecurePublicKey() {
+        return getCurrentEnvironment().getAllowInsecurePublicKey();
     }
 
     public Environment getCurrentEnvironment() {
-        return getEnvironmentByName(activeEnvironment);
+        if (environments.containsKey(activeEnvironment)) {
+            return environments.get(activeEnvironment);
+        } else {
+            throw new FitConnectInitialisationException("No environment with name '" + activeEnvironment.getName() + "' found. Available environments are: " + getAvailableEnvironmentNames());
+        }
     }
 
     public String getOAuthTokenEndpoint() {
-        return getCurrentEnvironment().getAuthBaseUrl() + ResourcePaths.AUTH_TOKEN_PATH;
+        return getAuthBaseUrl() + ResourcePaths.AUTH_TOKEN_PATH;
     }
 
     public String getSubmissionsEndpoint() {
@@ -113,7 +124,10 @@ public class ApplicationConfig {
     }
 
     private String getSubmissionBaseUrl() {
-        return getCurrentEnvironment().getSubmissionBaseUrl();
+        return getCurrentEnvironment().getSubmissionBaseUrls()
+                .stream()
+                .findFirst()
+                .orElseThrow(() -> new FitConnectInitialisationException("No submission base url found, expected at least one"));
     }
 
     private String getSelfServicePortalBaseUrl() {
@@ -124,6 +138,10 @@ public class ApplicationConfig {
         return getCurrentEnvironment().getRoutingBaseUrl();
     }
 
+    private String getAuthBaseUrl() {
+        return getCurrentEnvironment().getAuthBaseUrl();
+    }
+
     private String getAvailableEnvironmentNames() {
         return environments.keySet()
                 .stream()
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/Environment.java b/api/src/main/java/dev/fitko/fitconnect/api/config/Environment.java
index af65655666ce26b69963a1468f452a05a960d4b6..ddcae3873fbcb8628f4e8ee47749b1464367f6fc 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/Environment.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/Environment.java
@@ -4,14 +4,48 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.util.Collections;
+import java.util.List;
+
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 public class Environment {
+
     private String authBaseUrl;
     private String routingBaseUrl;
-    private String submissionBaseUrl;
+    private List<String> submissionBaseUrls = Collections.emptyList();
     private String selfServicePortalBaseUrl;
-    private boolean allowInsecurePublicKey;
-    private boolean skipSubmissionDataValidation;
+
+    private Boolean enableAutoReject = true;
+    private Boolean allowInsecurePublicKey = false;
+    private Boolean skipSubmissionDataValidation = false;
+
+    /**
+     * Merges current env with provided other env.
+     * If a field of the current env is null or empty, the value from other will be set.
+     *
+     * @param other environment to merge the current environment with
+     * @return merged environment
+     */
+    public Environment merge(final Environment other){
+
+        final Environment mergedEnv = new Environment();
+
+        mergedEnv.setAuthBaseUrl(isNullOrEmpty(authBaseUrl) ? other.getAuthBaseUrl() : authBaseUrl);
+        mergedEnv.setRoutingBaseUrl(isNullOrEmpty(routingBaseUrl) ? other.getRoutingBaseUrl() : routingBaseUrl);
+        mergedEnv.setSubmissionBaseUrls(submissionBaseUrls.isEmpty() ? other.getSubmissionBaseUrls() : submissionBaseUrls);
+        mergedEnv.setSelfServicePortalBaseUrl(isNullOrEmpty(selfServicePortalBaseUrl) ? other.getSelfServicePortalBaseUrl() : selfServicePortalBaseUrl);
+
+        mergedEnv.setEnableAutoReject(enableAutoReject == null ? other.getEnableAutoReject() : enableAutoReject);
+        mergedEnv.setAllowInsecurePublicKey(allowInsecurePublicKey == null ? other.getAllowInsecurePublicKey() : allowInsecurePublicKey);
+        mergedEnv.setSkipSubmissionDataValidation(skipSubmissionDataValidation == null ? other.getSkipSubmissionDataValidation() : skipSubmissionDataValidation);
+
+        return mergedEnv;
+    }
+
+    private boolean isNullOrEmpty(final String s){
+        return s == null || s.isEmpty();
+    }
+
 }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/ResourcePaths.java b/api/src/main/java/dev/fitko/fitconnect/api/config/ResourcePaths.java
deleted file mode 100644
index 367aee5f1e9241da95d258021e3e7ed58dd49c4f..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/ResourcePaths.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.fitko.fitconnect.api.config;
-
-final class ResourcePaths {
-
-    private ResourcePaths() {
-    }
-
-    static final String AUTH_TOKEN_PATH = "/token";
-
-    static final String DESTINATIONS_PATH = "/v1/destinations/%s";
-    static final String DESTINATIONS_KEY_PATH = "/v1/destinations/%s/keys/%s";
-
-    static final String EVENTS_PATH = "/v1/cases/%s/events";
-
-    static final String SUBMISSION_PATH = "/v1/submissions/%s";
-    static final String SUBMISSIONS_PATH = "/v1/submissions";
-    static final String SUBMISSION_ATTACHMENT_PATH = "/v1/submissions/%s/attachments/%s";
-
-    static final String ROUTING_AREA_PATH = "/v1/areas";
-    static final String ROUTING_ROUTE_PATH = "/v1/routes";
-
-    static final String WELL_KNOWN_KEYS_PATH = "/.well-known/jwks.json";
-}
-
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/BuildInfo.java b/api/src/main/java/dev/fitko/fitconnect/api/config/build/BuildInfo.java
similarity index 76%
rename from api/src/main/java/dev/fitko/fitconnect/api/config/BuildInfo.java
rename to api/src/main/java/dev/fitko/fitconnect/api/config/build/BuildInfo.java
index d73b03b823f7b9f73a7b305493ffe5ec930933e9..dac3c451b0486dd003e8d69f0df7f54571502163 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/BuildInfo.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/build/BuildInfo.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.config;
+package dev.fitko.fitconnect.api.config.build;
 
 import lombok.Data;
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/DefaultEnvironments.java b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/DefaultEnvironments.java
new file mode 100644
index 0000000000000000000000000000000000000000..73e1c377a5bb556c1629bb82495be152f985425e
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/DefaultEnvironments.java
@@ -0,0 +1,51 @@
+package dev.fitko.fitconnect.api.config.defaults;
+
+import dev.fitko.fitconnect.api.config.Environment;
+import dev.fitko.fitconnect.api.config.EnvironmentName;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Getter
+@AllArgsConstructor
+public enum DefaultEnvironments {
+
+    PROD(new EnvironmentName("PROD"), new Environment(
+            "https://auth-prod.fit-connect.fitko.net",
+            "https://routing-api-prod.fit-connect.fitko.net",
+            List.of("https://submission-api-prod.fit-connect.niedersachsen.de"),
+            "https://portal.auth-prod.fit-connect.fitko.net",
+            true,
+            false,
+            false)),
+
+    STAGE(new EnvironmentName("STAGE"), new Environment(
+            "https://auth-refz.fit-connect.fitko.net",
+            "https://routing-api-prod.fit-connect.fitko.net",
+            List.of("https://submission-api-refz.fit-connect.niedersachsen.de"),
+            "https://portal.auth-refz.fit-connect.fitko.net",
+            true,
+            false,
+            false)
+    ),
+
+    TEST(new EnvironmentName("TEST"), new Environment(
+            "https://auth-testing.fit-connect.fitko.dev",
+            "https://routing-api-testing.fit-connect.fitko.dev",
+            List.of("https://submission-api-testing.fit-connect.fitko.dev"),
+            "https://portal.auth-testing.fit-connect.fitko.dev",
+            true,
+            true,
+            false));
+
+    private final EnvironmentName environmentName;
+    private final Environment environment;
+
+    public static Map<EnvironmentName, Environment> getEnvironmentsAsMap(){
+        return Arrays.stream(values()).collect(Collectors.toMap(DefaultEnvironments::getEnvironmentName, DefaultEnvironments::getEnvironment));
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/ResourcePaths.java b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/ResourcePaths.java
new file mode 100644
index 0000000000000000000000000000000000000000..29614c66cb938e597fc1edff93771965c33f184f
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/ResourcePaths.java
@@ -0,0 +1,24 @@
+package dev.fitko.fitconnect.api.config.defaults;
+
+public final class ResourcePaths {
+
+    private ResourcePaths() {
+    }
+
+    public static final String AUTH_TOKEN_PATH = "/token";
+
+    public static final String DESTINATIONS_PATH = "/v1/destinations/{destinationId}";
+    public static final String DESTINATIONS_KEY_PATH = "/v1/destinations/{destinationId}/keys/{kid}";
+
+    public static final String EVENTS_PATH = "/v1/cases/{caseId}/events";
+
+    public static final String SUBMISSION_PATH = "/v1/submissions/{submissionId}";
+    public static final String SUBMISSIONS_PATH = "/v1/submissions";
+    public static final String SUBMISSION_ATTACHMENT_PATH = "/v1/submissions/{submissionId}/attachments/{attachmentId}";
+
+    public static final String ROUTING_AREA_PATH = "/v1/areas";
+    public static final String ROUTING_ROUTE_PATH = "/v1/routes";
+
+    public static final String WELL_KNOWN_KEYS_PATH = "/.well-known/jwks.json";
+}
+
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/SchemaConfig.java
similarity index 97%
rename from api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java
rename to api/src/main/java/dev/fitko/fitconnect/api/config/defaults/SchemaConfig.java
index b1d3c0d49c2823d3b261acc212514b5b57922005..6c9e66c16b85c5d9833e950f173702b9c883066d 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/config/SchemaConfig.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/config/defaults/SchemaConfig.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.config;
+package dev.fitko.fitconnect.api.config.defaults;
 
 import lombok.Getter;
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/Event.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/Event.java
index 3a1bf7c1c9676ed697efa2a6f26ecd5de562a454..ce8321917bfbb9bcbe702eb195cbd80b95274c29 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/Event.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/model/event/Event.java
@@ -4,7 +4,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
-import static dev.fitko.fitconnect.api.config.SchemaConfig.EVENTS_SCHEMA_PATH;
+import static dev.fitko.fitconnect.api.config.defaults.SchemaConfig.EVENTS_SCHEMA_PATH;
 
 public enum Event {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
index 9448e3ad0bc9e2dd8c82ffca50685f0b3a4eb1b5..058911a4c930bb426823e808758c95d10e59e5c0 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationContext.java
@@ -1,7 +1,7 @@
 package dev.fitko.fitconnect.api.domain.validation;
 
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import lombok.Getter;
 
 import java.util.ArrayList;
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
index 129780e15ceb875ba1a9538599e2ccd3716bd26a..be37b33edb89977f9a9d6a53ab9fee14853d4ddf 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/domain/validation/ValidationResult.java
@@ -1,6 +1,7 @@
 package dev.fitko.fitconnect.api.domain.validation;
 
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -61,6 +62,15 @@ public final class ValidationResult {
         return new ValidationResult(false, exception);
     }
 
+    /**
+     * Create new failed result with an error message that's wrapped in a ValidationException.
+     *
+     * @return the invalid result
+     */
+    public static ValidationResult error(final String errorMessage) {
+        return new ValidationResult(false, new ValidationException(errorMessage));
+    }
+
     /**
      * Create new failed result with a {@link Problem}.
      *
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentCreationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentCreationException.java
deleted file mode 100644
index c510597c7acdfff01460919a315ad29042c3db9c..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentCreationException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class AttachmentCreationException extends RuntimeException {
-
-    public AttachmentCreationException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-
-    public AttachmentCreationException(final String errorMessage) {
-        super(errorMessage);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentUploadException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentUploadException.java
deleted file mode 100644
index cc12d3ac7a33ea2067f12b622020c1e960ec5d47..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AttachmentUploadException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class AttachmentUploadException extends RuntimeException {
-
-    public AttachmentUploadException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationException.java
deleted file mode 100644
index 0091336dd9c96d676741cc0d990e1e953ccb3f7f..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class AuthenticationException extends RuntimeException {
-
-    public AuthenticationException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataNotPresentException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataNotPresentException.java
deleted file mode 100644
index b9914ed451f32dad632ba4c25bece1d1db5590c6..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataNotPresentException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class DataNotPresentException extends RuntimeException {
-
-    public DataNotPresentException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-
-    public DataNotPresentException(final String errorMessage) {
-        super(errorMessage);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/FileHandlingException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/FileHandlingException.java
deleted file mode 100644
index dfe704d8d1b0e97e652029ba1619fa7294cade56..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/FileHandlingException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class FileHandlingException extends RuntimeException {
-
-    public FileHandlingException(Exception exception) {
-        super(exception);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/InitializationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/InitializationException.java
deleted file mode 100644
index 3c723047701e37ff8c1b0991eedaaf12a9461e08..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/InitializationException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class InitializationException extends RuntimeException {
-
-    public InitializationException(final String errorMessage) {
-        super(errorMessage);
-    }
-
-    public InitializationException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java
deleted file mode 100644
index 3858e304602a571b20eaef89dd61a0e6358c1b32..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/KeyNotRetrievedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class KeyNotRetrievedException extends RuntimeException {
-
-    public KeyNotRetrievedException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-
-    public KeyNotRetrievedException(final String errorMessage) {
-        super(errorMessage);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java
deleted file mode 100644
index 6a4eb61a666e3d794c76c82c82d0280cb267444a..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/MetadataNotCreatedException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class MetadataNotCreatedException extends RuntimeException {
-
-    public MetadataNotCreatedException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RoutingException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RoutingException.java
deleted file mode 100644
index ad334471f971fc361883cc03781fdd37b6b21f36..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RoutingException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class RoutingException extends RuntimeException {
-
-    public RoutingException(final String errorMessage) {
-        super(errorMessage);
-    }
-
-    public RoutingException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java
deleted file mode 100644
index 2a41fa6835b6f7ffa5057599b9d7ae1f86b52446..0000000000000000000000000000000000000000
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionNotCreatedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package dev.fitko.fitconnect.api.exceptions;
-
-public class SubmissionNotCreatedException extends RuntimeException {
-
-    public SubmissionNotCreatedException(final String errorMessage, final Throwable error) {
-        super(errorMessage, error);
-    }
-
-    public SubmissionNotCreatedException(final String errorMessage) {
-        super(errorMessage);
-    }
-}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectInitialisationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectInitialisationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f52f617849f4fbdae7351ef83f367f22ae37289d
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectInitialisationException.java
@@ -0,0 +1,12 @@
+package dev.fitko.fitconnect.api.exceptions.client;
+
+public class FitConnectInitialisationException extends RuntimeException {
+
+    public FitConnectInitialisationException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+    public FitConnectInitialisationException(final String errorMessage, final Throwable error) {
+        super(errorMessage, error);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectRouterException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectRouterException.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f5ce3db91f80a12a74811b4b2a223a2f49225e9
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectRouterException.java
@@ -0,0 +1,13 @@
+package dev.fitko.fitconnect.api.exceptions.client;
+
+public class FitConnectRouterException extends RuntimeException {
+
+    public FitConnectRouterException(final String errorMessage, final Throwable throwable) {
+        super(errorMessage, throwable);
+    }
+
+    public FitConnectRouterException(final String errorMessage) {
+        super(errorMessage);
+    }
+
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSenderException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSenderException.java
new file mode 100644
index 0000000000000000000000000000000000000000..896a07e20a2e280afbdbc60fe70261bbd7cbdd77
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSenderException.java
@@ -0,0 +1,8 @@
+package dev.fitko.fitconnect.api.exceptions.client;
+
+public class FitConnectSenderException extends RuntimeException {
+
+    public FitConnectSenderException(final String errorMessage, final Throwable throwable) {
+        super(errorMessage, throwable);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSubscriberException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSubscriberException.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b3a951591b23cff21b1de3815f8978c8d071d18
--- /dev/null
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/client/FitConnectSubscriberException.java
@@ -0,0 +1,8 @@
+package dev.fitko.fitconnect.api.exceptions.client;
+
+public class FitConnectSubscriberException extends RuntimeException {
+
+    public FitConnectSubscriberException(final String errorMessage, final Throwable throwable) {
+        super(errorMessage, throwable);
+    }
+}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/AuthenticationTagsEmptyException.java
similarity index 85%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/AuthenticationTagsEmptyException.java
index 8b4ad53aa037dc3cde571b77dde849582a952d83..b0bb330ca24fdcae71b526be582685cb8d4172d9 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/AuthenticationTagsEmptyException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/AuthenticationTagsEmptyException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class AuthenticationTagsEmptyException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/BatchImportException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/BatchImportException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/BatchImportException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/BatchImportException.java
index c91e7437063a1592dd8237c160a88156e16205c5..d65a9c0d8ac2bb77bbae30a7b4d2f4f5ec1f2b0e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/BatchImportException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/BatchImportException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class BatchImportException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataIntegrityException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DataIntegrityException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataIntegrityException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DataIntegrityException.java
index bbd29906d8ba74272968f4d30289e14dc3b7af80..e17a9ae8d4c42db61a638bef7ff40e6447253f0e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DataIntegrityException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DataIntegrityException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class DataIntegrityException extends Exception {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DecryptionException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DecryptionException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/DecryptionException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DecryptionException.java
index 37174195c1f879481304f9104d2c1a991933dcd7..ff15c62f7cf5d120eea2d0a36224f773130c046a 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/DecryptionException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/DecryptionException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class DecryptionException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EncryptionException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EncryptionException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/EncryptionException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EncryptionException.java
index a9a780966eb91a7a5b4c01d87c463c64bd68b3da..64a786aa81c9ef62487b511d80279c98c2a78c7d 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EncryptionException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EncryptionException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class EncryptionException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventCreationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventCreationException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventCreationException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventCreationException.java
index 41e4c0d94b3889200348a301868e0426bff5a391..3a02e0bc22af5d58be8125b5378016d4d77dce0f 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventCreationException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventCreationException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class EventCreationException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventLogException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventLogException.java
similarity index 83%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventLogException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventLogException.java
index d47bdc98396880ee8dfa6fc12f11f863cb841501..9e2ae46efb2ef6265886378505d5f31419c555b3 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/EventLogException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/EventLogException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class EventLogException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/InvalidKeyException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/InvalidKeyException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/InvalidKeyException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/InvalidKeyException.java
index 52cb4aa6bbbe21eeda985e252518c88103b08613..0571c1cb8d3cce5afb92d77ddb71269d7b156a2f 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/InvalidKeyException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/InvalidKeyException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class InvalidKeyException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RestApiException.java
similarity index 93%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RestApiException.java
index bbefe15ed5db248b3f7738422ed16c1980af2b46..a1aee1f01fa8db6de81d61449c05ab3508d84386 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RestApiException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RestApiException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class RestApiException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RootCertificateException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RootCertificateException.java
similarity index 82%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/RootCertificateException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RootCertificateException.java
index 4d888dab142cdb285747c2f0e10a93bd843f701a..568f9e11899106d1ebfd2a4605d8620a58b3d04e 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/RootCertificateException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/RootCertificateException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class RootCertificateException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SchemaNotFoundException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SchemaNotFoundException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/SchemaNotFoundException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SchemaNotFoundException.java
index f7c4b982c102ad6f692cd8c9c93c5d1c9b168c1c..43bd955964276e3eb8bd7bd502d45385be52b1e5 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SchemaNotFoundException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SchemaNotFoundException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class SchemaNotFoundException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmissionRequestException.java
similarity index 85%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmissionRequestException.java
index 708c3355cbee2f243e09dd4f6771d37d8f48fe83..2202a1a9fb0abc9b3bcb065b40cc0ceb9b35ede6 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmissionRequestException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmissionRequestException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class SubmissionRequestException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmitEventNotFoundException.java
similarity index 85%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmitEventNotFoundException.java
index 5075c4886298d75067ea9de71fcef7348c1a89c0..52d7a91f0c43b53df5a2afb76288306e32bd25b5 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/SubmitEventNotFoundException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/SubmitEventNotFoundException.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class SubmitEventNotFoundException extends RuntimeException {
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/ValidationException.java b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/ValidationException.java
similarity index 84%
rename from api/src/main/java/dev/fitko/fitconnect/api/exceptions/ValidationException.java
rename to api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/ValidationException.java
index 3113bb7807e78e3b93d7548ac8bf06fbda2632b6..941d757e2c167b0791278d32dde057fbc06693e7 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/exceptions/ValidationException.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/exceptions/internal/ValidationException.java
@@ -1,8 +1,7 @@
-package dev.fitko.fitconnect.api.exceptions;
+package dev.fitko.fitconnect.api.exceptions.internal;
 
 public class ValidationException extends RuntimeException {
 
-
     public ValidationException(final String errorMessage) {
         super(errorMessage);
     }
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
index b6255764d53ae6fe9d91e83cdd947cd072f4878a..e5534d1ee621afd5529e17eda05a162574797aed 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/Sender.java
@@ -4,7 +4,6 @@ import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
-import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
 import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
@@ -13,9 +12,9 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.domain.model.submission.*;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.*;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 
 import java.net.URI;
 import java.util.List;
@@ -85,9 +84,8 @@ public interface Sender {
      * @param submission with a destinationId, a list of attachmentIds and a serviceType
      * @return the announced submission
      *
-     * @throws SubmissionNotCreatedException if the announcement of the submission failed, e.g. due to a technical problem
      */
-    SubmissionForPickup createSubmission(CreateSubmission submission) throws SubmissionNotCreatedException;
+    SubmissionForPickup createSubmission(CreateSubmission submission);
 
     /**
      * Uploads encrypted {@link ApiAttachment}s to for given submission id.
@@ -96,7 +94,6 @@ public interface Sender {
      * @param attachmentId unique identifier of the attachment
      * @param encryptedAttachment string of JWE encrypted attachment
      *
-     * @throws AttachmentUploadException if a technical problem occurred upload the attachment
      */
     void uploadAttachment(UUID submissionId, UUID attachmentId, String encryptedAttachment);
 
@@ -115,9 +112,8 @@ public interface Sender {
      *
      * @return the public key for encrypting {@link Metadata}, {@link Data} and {@link ApiAttachment}s
      *
-     * @throws KeyNotRetrievedException if a technical problem occurred fetching the key
      */
-    RSAKey getEncryptionKeyForDestination(UUID destinationId) throws KeyNotRetrievedException;
+    RSAKey getEncryptionKeyForDestination(UUID destinationId);
 
     /**
      * Send a {@link SubmitSubmission} that includes encrypted {@link Metadata} and {@link Data}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java b/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
index 29db8291a4a9b77f93d4725b9ceeb467569931db..f82b50132e50b8bb28131b34902f6dd14f4cffc0 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/Subscriber.java
@@ -14,8 +14,8 @@ import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 
 import java.net.URI;
 import java.util.List;
@@ -147,6 +147,7 @@ public interface Subscriber {
      * Sends a confirmation event if the received submission matches all validations.
      *
      * @param eventPayload contains submissionId, caseId, destination and a list of problems
+
      * @see <a href="https://docs.fitko.de/fit-connect/docs/receiving/process-and-acknowledge">Process And Acknowledge</a>
      */
     void acceptSubmission(EventPayload eventPayload);
@@ -155,6 +156,7 @@ public interface Subscriber {
      * Sends a rejection event if the received submission violates any validation rule.
      *
      * @param eventPayload contains submissionId, caseId, destination and a list of problems
+
      * @see <a href="https://docs.fitko.de/fit-connect/docs/receiving/process-and-acknowledge">Process And Acknowledge</a>
      */
     void rejectSubmission(EventPayload eventPayload);
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/auth/OAuthService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/auth/OAuthService.java
index c553dd0f55945bf01fa2ca8380c5c862ae1f99b5..685e577cc5440303bb78350dd375a09162989ac5 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/auth/OAuthService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/auth/OAuthService.java
@@ -3,7 +3,7 @@ package dev.fitko.fitconnect.api.services.auth;
 import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 
 /**
  * A service that provides an interface to authenticate against the FIT-Connect API in order
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/crypto/CryptoService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/crypto/CryptoService.java
index fb6d70e73f17a1ea0b7959d297a3e5f9ff675124..890ffc9f14ac68f9d8e675173e8ebd98be82562f 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/crypto/CryptoService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/crypto/CryptoService.java
@@ -4,8 +4,8 @@ import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
 
 /**
  * A service that allows to encrypt and decrypt {@link Data} and {@link ApiAttachment}s of a {@link SubmitSubmission}
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
index d319f317dcbf277ee90f971d0f05aac7cd04920b..ded5f5a1a3f035bbfee219262c1b5ed189a0ec9a 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/events/EventLogService.java
@@ -7,7 +7,7 @@ import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
 
 import java.util.List;
 import java.util.UUID;
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/events/SecurityEventService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/events/SecurityEventService.java
index 4ec5ce6d97615df76a75a82ddb0608e2d43012ff..3ff98c30180baee8e002c74944b7b42923e9ea23 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/events/SecurityEventService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/events/SecurityEventService.java
@@ -4,7 +4,7 @@ import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.domain.model.event.EventLog;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
 
 /**
  *  A service that creates SET events that are added to a {@link Submission}s {@link EventLog}.
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/routing/RoutingService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/routing/RoutingService.java
index d398df4511dffc54be443b6da13ab7608169fa11..fe8b64d16d2856db12572d1e1db3adf9218f9f44 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/routing/RoutingService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/routing/RoutingService.java
@@ -4,7 +4,7 @@ import dev.fitko.fitconnect.api.domain.model.route.Area;
 import dev.fitko.fitconnect.api.domain.model.route.AreaResult;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
 import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 
 import java.util.List;
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/schema/SchemaProvider.java b/api/src/main/java/dev/fitko/fitconnect/api/services/schema/SchemaProvider.java
index af69d63c2ee5263af2f30835720158d25cef30cf..2874cda531cd9d84cb19a93eb37fb83294733b34 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/schema/SchemaProvider.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/schema/SchemaProvider.java
@@ -1,7 +1,7 @@
 package dev.fitko.fitconnect.api.services.schema;
 
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.SchemaNotFoundException;
 
 import java.net.URI;
 
diff --git a/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java b/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
index 5f49f41da3c6b6bff5c30ef75f3bf1f5e3c1308e..dd8277053dfa7616ebcf827df1d009e874288428 100644
--- a/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
+++ b/api/src/main/java/dev/fitko/fitconnect/api/services/submission/SubmissionService.java
@@ -10,7 +10,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionsForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 
 import java.util.UUID;
 
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/RouterClient.java b/client/src/main/java/dev/fitko/fitconnect/client/RouterClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..7386001362beeaae85cd39f57eed15e9eb135354
--- /dev/null
+++ b/client/src/main/java/dev/fitko/fitconnect/client/RouterClient.java
@@ -0,0 +1,84 @@
+package dev.fitko.fitconnect.client;
+
+import dev.fitko.fitconnect.api.domain.model.route.Area;
+import dev.fitko.fitconnect.api.domain.model.route.Route;
+import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
+import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectRouterException;
+import dev.fitko.fitconnect.api.services.routing.RoutingService;
+import dev.fitko.fitconnect.client.router.DestinationSearch;
+import dev.fitko.fitconnect.core.routing.RouteVerifier;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public final class RouterClient {
+
+    private final RoutingService routingService;
+    private final RouteVerifier routeVerifier;
+
+    public RouterClient(final RoutingService routingService, final RouteVerifier routeVerifier) {
+        this.routingService = routingService;
+        this.routeVerifier = routeVerifier;
+    }
+
+    /**
+     * Finds a list of {@link Route}s by a given service identifier and an area search criterion.
+     *
+     * @param search search parameters that contain leikaKey and one other area search criterion see {@link DestinationSearch}
+     * @return list of found {@link Route}s matching the search criteria
+     * @throws FitConnectRouterException if the signature validation failed or a technical error occurred
+     */
+    public List<Route> findDestinations(final DestinationSearch search) throws FitConnectRouterException {
+
+        final String leikaKey = search.getLeikaKey();
+        final String ars = search.getArs();
+        final String ags = search.getAgs();
+        final String areaId = search.getAreaId();
+        final int offset = search.getOffset();
+        final int limit = search.getLimit();
+
+        final RouteResult routeResult = wrapExceptions(() -> routingService.getRoutes(leikaKey, ars, ags, areaId, offset, limit), "Finding destination failed");
+        final ValidationResult validationResult = routeVerifier.validateRouteDestinations(routeResult.getRoutes(), leikaKey, ars);
+
+        if (validationResult.hasError()) {
+            throw new FitConnectRouterException(validationResult.getError().getMessage(), validationResult.getError());
+        }
+
+        return routeResult.getRoutes();
+    }
+
+    /**
+     * Finds a list of {@link Area}s based on a filter criterion, e.g. a zip code or city.
+     *
+     * @param filter filter criterion as string
+     * @param offset offset to start from
+     * @param limit  max entries
+     * @return list of {@link Area}
+     * @throws FitConnectRouterException if a technical error occurred during the query
+     */
+    public List<Area> findAreas(final String filter, final int offset, final int limit) throws FitConnectRouterException {
+        return wrapExceptions(() -> findAreas(List.of(filter), offset, limit), "Finding areas failed");
+    }
+
+    /**
+     * Find a list {@link Area}s based on a list of multiple filter criteria, e.g. a zip code or a city as in List.of("04229", "Leip*").
+     *
+     * @param filters list of string filters
+     * @param offset  offset to start from
+     * @param limit   max entries
+     * @return list of {@link Area}
+     * @throws FitConnectRouterException if a technical error occurred during the query
+     */
+    public List<Area> findAreas(final List<String> filters, final int offset, final int limit) throws FitConnectRouterException {
+        return wrapExceptions(() -> routingService.getAreas(filters, offset, limit).getAreas(), "Finding areas failed");
+    }
+
+    private <T> T wrapExceptions(final Supplier<T> supplier, final String errorMessage) {
+        try {
+            return supplier.get();
+        } catch (final Exception e) {
+            throw new FitConnectRouterException(errorMessage, e.getCause() != null ? e.getCause() : e);
+        }
+    }
+}
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java b/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java
deleted file mode 100644
index c4438300daff840e8dcc9090eb3688004a22651b..0000000000000000000000000000000000000000
--- a/client/src/main/java/dev/fitko/fitconnect/client/RoutingClient.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package dev.fitko.fitconnect.client;
-
-import dev.fitko.fitconnect.api.domain.model.route.Area;
-import dev.fitko.fitconnect.api.domain.model.route.Route;
-import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
-import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.RoutingException;
-import dev.fitko.fitconnect.api.services.routing.RoutingService;
-import dev.fitko.fitconnect.client.router.DestinationSearch;
-import dev.fitko.fitconnect.core.routing.RouteVerifier;
-
-import java.util.List;
-
-public final class RoutingClient {
-
-    private final RoutingService routingService;
-    private final RouteVerifier routeVerifier;
-
-    public RoutingClient(final RoutingService routingService, final RouteVerifier routeVerifier) {
-        this.routingService = routingService;
-        this.routeVerifier = routeVerifier;
-    }
-
-    /**
-     * Finds a list of {@link Route}s by a given service identifier and an area search criterion.
-     *
-     * @param search search parameters that contain leikaKey and one other area search criterion see {@link DestinationSearch}
-     * @return list of found {@link Route}s matching the search criteria
-     * @throws RoutingException if the signature validation failed
-     * @throws RestApiException if a technical error occurred during the query
-     */
-    public List<Route> findDestinations(final DestinationSearch search) throws RoutingException, RestApiException {
-        final RouteResult routeResult = routingService.getRoutes(search.getLeikaKey(), search.getArs(), search.getAgs(), search.getAreaId(), search.getOffset(), search.getLimit());
-        final ValidationResult result = routeVerifier.validateRouteDestinations(routeResult.getRoutes(), search.getLeikaKey(), search.getArs());
-        if (result.hasError()) {
-            throw new RoutingException(result.getError().getMessage(), result.getError());
-        }
-        return routeResult.getRoutes();
-    }
-
-    /**
-     * Finds a list of {@link Area}s based on a filter criterion, e.g. a zip code or city.
-     *
-     * @param filter filter criterion as string
-     * @param offset offset to start from
-     * @param limit  max entries
-     * @return list of {@link Area}
-     * @throws RestApiException if a technical error occurred during the query
-     */
-    public List<Area> findAreas(final String filter, final int offset, final int limit) throws RestApiException {
-        return findAreas(List.of(filter), offset, limit);
-    }
-
-    /**
-     * Find a list {@link Area}s based on a list of multiple filter criteria, e.g. a zip code or a city as in List.of("04229", "Leip*").
-     *
-     * @param filters list of string filters
-     * @param offset  offset to start from
-     * @param limit   max entries
-     * @return list of {@link Area}
-     * @throws RestApiException if a technical error occurred during the query
-     */
-    public List<Area> findAreas(final List<String> filters, final int offset, final int limit) throws RestApiException {
-        return routingService.getAreas(filters, offset, limit).getAreas();
-    }
-}
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java b/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
index 997d2c8e567b0e113ca1767a8c396153d7943cf9..89cdcb545848a690878d3e5f147c043d96fc6abf 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/SenderClient.java
@@ -6,8 +6,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
-import dev.fitko.fitconnect.api.exceptions.KeyNotRetrievedException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSenderException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
@@ -16,6 +15,7 @@ import dev.fitko.fitconnect.client.sender.strategies.SendNewSubmissionStrategy;
 import dev.fitko.fitconnect.client.util.ValidDataGuard;
 
 import java.util.UUID;
+import java.util.function.Supplier;
 
 /**
  * A sender-side client for announcing and handing in a {@link SubmitSubmission}
@@ -36,11 +36,10 @@ public class SenderClient {
      *
      * @param destinationId unique identifier of a {@link Destination}
      * @return optional string containing the public JWK
-     * @throws KeyNotRetrievedException is there was a technical problem retrieving the key
-     * @throws InvalidKeyException      if the key validation failed
+     * @throws FitConnectSenderException if a technical or error occurred retrieving the key or a validation failed
      */
-    public String getPublicKeyForDestination(final UUID destinationId) throws KeyNotRetrievedException, InvalidKeyException {
-        return sender.getEncryptionKeyForDestination(destinationId).toJSONString();
+    public String getPublicKeyForDestination(final UUID destinationId) throws FitConnectSenderException {
+        return wrapExceptions(() -> sender.getEncryptionKeyForDestination(destinationId).toJSONString(), "Retrieving public key failed.");
     }
 
     /**
@@ -48,9 +47,10 @@ public class SenderClient {
      *
      * @param sentSubmission the original {@link SentSubmission} the status should be retrieved for
      * @return {@link SubmissionStatus} the current status
+     * @throws FitConnectSenderException if a technical or error occurred during status retrieval or a validation failed
      */
-    public SubmissionStatus getStatusForSubmission(final SentSubmission sentSubmission) {
-        return sender.getLastedEvent(sentSubmission);
+    public SubmissionStatus getStatusForSubmission(final SentSubmission sentSubmission) throws FitConnectSenderException {
+        return wrapExceptions(() -> sender.getLastedEvent(sentSubmission), "Retrieving current status of submission failed.");
     }
 
     /**
@@ -61,28 +61,43 @@ public class SenderClient {
      * @param httpBody       HTTP body provided by the callback
      * @param callbackSecret secret owned by the client, which is used to calculate the hmac
      * @return {@code ValidationResult.ok()} if hmac and timestamp provided by the callback meet the required conditions
+     * @throws FitConnectSenderException if a technical error occurred during the validation
      */
-    public ValidationResult validateCallback(final String hmac, final Long timestamp, final String httpBody, final String callbackSecret) {
-        return sender.validateCallback(hmac, timestamp, httpBody, callbackSecret);
+    public ValidationResult validateCallback(final String hmac, final Long timestamp, final String httpBody, final String callbackSecret) throws FitConnectSenderException {
+        return wrapExceptions(() -> sender.validateCallback(hmac, timestamp, httpBody, callbackSecret), "Callback validation could not be applied.");
     }
 
     /**
      * Send submission with attachments and data to FIT-Connect API. Encryption is handled by the SDK.
      *
      * @return {@link SentSubmission} object of the handed in submission
+     * @throws FitConnectSenderException if a technical error occurred during sending or a validation failed
      */
-    public SentSubmission send(final SendableSubmission sendableSubmission) {
-        dataGuard.ensureValidDataPayload(sendableSubmission);
-        return new SendNewSubmissionStrategy(sender).send(sendableSubmission);
+    public SentSubmission send(final SendableSubmission sendableSubmission) throws FitConnectSenderException {
+        return wrapExceptions(() -> {
+            dataGuard.ensureValidDataPayload(sendableSubmission);
+            return new SendNewSubmissionStrategy(sender).send(sendableSubmission);
+        }, "Sending submission failed.");
     }
 
     /**
      * Send already encrypted submission to FIT-Connect API.
      *
      * @return {@link SentSubmission} object of the handed in submission
+     * @throws FitConnectSenderException if a technical error occurred during sending or a validation failed
      */
-    public SentSubmission send(final SendableEncryptedSubmission sendableEncryptedSubmission) {
-        dataGuard.ensureValidDataPayload(sendableEncryptedSubmission);
-        return new SendEncryptedSubmissionStrategy(sender).send(sendableEncryptedSubmission);
+    public SentSubmission send(final SendableEncryptedSubmission sendableEncryptedSubmission) throws FitConnectSenderException {
+        return wrapExceptions(() -> {
+            dataGuard.ensureValidDataPayload(sendableEncryptedSubmission);
+            return new SendEncryptedSubmissionStrategy(sender).send(sendableEncryptedSubmission);
+        }, "Sending already encrypted submission failed.");
+    }
+
+    private <T> T wrapExceptions(final Supplier<T> supplier, final String errorMessage) {
+        try {
+            return supplier.get();
+        } catch (final Exception e) {
+            throw new FitConnectSenderException(errorMessage, e.getCause() != null ? e.getCause() : e);
+        }
     }
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java b/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
index 932cad122d9cdf148bf8af9388efd2bdff0604df..5fb0258b2527dbbaa3c1bb00b47f1045aa708cb1 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/SubscriberClient.java
@@ -12,7 +12,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSubscriberException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
@@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Supplier;
 
 /**
  * A subscriber-side client for retrieving submissions.
@@ -46,8 +47,9 @@ public class SubscriberClient {
      *
      * @param destinationId filter criterion for the submissions
      * @return set of the first 0..500 available submissions
+     * @throws FitConnectSubscriberException if a technical error occurred retrieving the list of available submissions
      */
-    public Set<SubmissionForPickup> getAvailableSubmissionsForDestination(final UUID destinationId) {
+    public Set<SubmissionForPickup> getAvailableSubmissionsForDestination(final UUID destinationId) throws FitConnectSubscriberException {
         return getAvailableSubmissionsForDestination(destinationId, 0, DEFAULT_SUBMISSION_LIMIT);
     }
 
@@ -58,9 +60,10 @@ public class SubscriberClient {
      * @param offset        position in the dataset
      * @param limit         number of submissions in result (max. is 500)
      * @return set of available submissions in the given range
+     * @throws FitConnectSubscriberException if a technical error occurred retrieving the list of available submissions
      */
-    public Set<SubmissionForPickup> getAvailableSubmissionsForDestination(final UUID destinationId, final int offset, final int limit) {
-        final Set<SubmissionForPickup> submissions = subscriber.pollAvailableSubmissionsForDestination(destinationId, offset, limit);
+    public Set<SubmissionForPickup> getAvailableSubmissionsForDestination(final UUID destinationId, final int offset, final int limit) throws FitConnectSubscriberException {
+        final Set<SubmissionForPickup> submissions = wrapExceptions(() -> subscriber.pollAvailableSubmissionsForDestination(destinationId, offset, limit), "Submissions could not be retrieved");
         LOGGER.info("Received {} submission(s) for destination {}", submissions.size(), destinationId);
         return submissions;
     }
@@ -71,8 +74,9 @@ public class SubscriberClient {
      * @param submission {@link SentSubmission} that is requested
      * @return {@link ReceivedSubmission} to get the submission {@link Metadata}, {@link Data} and {@link ApiAttachment}s as well as
      * accept or reject the loaded submission
+     * @throws FitConnectSubscriberException if a technical error occurred or validation failed
      */
-    public ReceivedSubmission requestSubmission(final SubmissionForPickup submission) {
+    public ReceivedSubmission requestSubmission(final SubmissionForPickup submission) throws FitConnectSubscriberException {
         return requestSubmission(submission.getSubmissionId());
     }
 
@@ -81,10 +85,10 @@ public class SubscriberClient {
      *
      * @param submissionId unique identifier of the requested submission
      * @return {@link ReceivedSubmission} to get the submission {@link Metadata}, {@link Data} and {@link Attachment}s as well as accept or reject the loaded submission
-     * @throws SubmissionRequestException if a technical error occurred or validation failed
+     * @throws FitConnectSubscriberException if a technical error occurred or validation failed
      */
-    public ReceivedSubmission requestSubmission(final UUID submissionId) throws SubmissionRequestException {
-        return submissionReceiver.receiveSubmission(submissionId);
+    public ReceivedSubmission requestSubmission(final UUID submissionId) throws FitConnectSubscriberException {
+        return wrapExceptions(() -> submissionReceiver.receiveSubmission(submissionId), "Submission could not be retrieved");
     }
 
     /**
@@ -92,10 +96,14 @@ public class SubscriberClient {
      *
      * @param submissionForPickup the submission that should be rejected
      * @param rejectionProblems   list of {@link Problem}s that give more detailed information on why the submission was rejected
+     * @throws FitConnectSubscriberException if a technical error occurred rejecting the submission
      * @see <a href="https://docs.fitko.de/fit-connect/docs/getting-started/event-log/events">FIT-Connect Events</a>
      */
-    public void rejectSubmission(final SubmissionForPickup submissionForPickup, final List<Problem> rejectionProblems) {
-        subscriber.rejectSubmission(EventPayload.forRejectEvent(submissionForPickup, rejectionProblems));
+    public void rejectSubmission(final SubmissionForPickup submissionForPickup, final List<Problem> rejectionProblems) throws FitConnectSubscriberException {
+        wrapExceptions(() -> {
+            subscriber.rejectSubmission(EventPayload.forRejectEvent(submissionForPickup, rejectionProblems));
+            return null;
+        }, "Could not reject submission");
     }
 
     /**
@@ -106,21 +114,30 @@ public class SubscriberClient {
      * @param httpBody       HTTP body provided by the callback
      * @param callbackSecret secret owned by the client, which is used to calculate the hmac
      * @return {@code true} if hmac and timestamp provided by the callback meet the required conditions
+     * @throws FitConnectSubscriberException if a technical error occurred during the validation
      */
-    public ValidationResult validateCallback(final String hmac, final Long timestamp, final String httpBody, final String callbackSecret) {
-        return subscriber.validateCallback(hmac, timestamp, httpBody, callbackSecret);
+    public ValidationResult validateCallback(final String hmac, final Long timestamp, final String httpBody, final String callbackSecret) throws FitConnectSubscriberException {
+        return wrapExceptions(() -> subscriber.validateCallback(hmac, timestamp, httpBody, callbackSecret), "Callback validation failed");
     }
 
     /**
      * Retrieve the current status of a {@link Submission}.
      *
      * @param destinationId unique identifier of the {@link Destination} the status should be retrieved for
-     * @param caseId unique identifier of the case the status should be retrieved for
-     * @param submissionId unique identifier of the submission the status should be retrieved for
-     *
+     * @param caseId        unique identifier of the case the status should be retrieved for
+     * @param submissionId  unique identifier of the submission the status should be retrieved for
      * @return {@link SubmissionStatus} the current status
+     * @throws FitConnectSubscriberException if a technical error occurred during the status request
      */
-    public SubmissionStatus getStatusForSubmission(final UUID destinationId, final UUID caseId, final UUID submissionId) {
-        return subscriber.getLastedEvent(destinationId, caseId, submissionId);
+    public SubmissionStatus getStatusForSubmission(final UUID destinationId, final UUID caseId, final UUID submissionId) throws FitConnectSubscriberException {
+        return wrapExceptions(() -> subscriber.getLastedEvent(destinationId, caseId, submissionId), "Status for submission not available");
+    }
+
+    private <T> T wrapExceptions(final Supplier<T> supplier, final String errorMessage) {
+        try {
+            return supplier.get();
+        } catch (final Exception e) {
+            throw new FitConnectSubscriberException(errorMessage, e.getCause() != null ? e.getCause() : e);
+        }
     }
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoader.java b/client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b2cff42e9fb8470a577ba2c2576d01ded54c70a
--- /dev/null
+++ b/client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoader.java
@@ -0,0 +1,101 @@
+package dev.fitko.fitconnect.client.bootstrap;
+
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.config.defaults.DefaultEnvironments;
+import dev.fitko.fitconnect.api.config.Environment;
+import dev.fitko.fitconnect.api.config.EnvironmentName;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.util.Map;
+
+public final class ApplicationConfigLoader {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfigLoader.class);
+    private static final String CONFIG_ENV_KEY_NAME = "FIT_CONNECT_CONFIG";
+
+    private ApplicationConfigLoader() {
+    }
+
+    /**
+     * Load ApplicationConfig from path.
+     *
+     * @param configPath path to config file
+     * @return ApplicationConfig
+     * @throws FitConnectInitialisationException if the config file could not be loaded
+     */
+    public static ApplicationConfig loadConfigFromPath(final Path configPath) {
+        try {
+            return loadConfigFromYamlString(Files.readString(configPath));
+        } catch (final IOException | NullPointerException e) {
+            LOGGER.error("config file could not be loaded from path {}", configPath);
+            throw new FitConnectInitialisationException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Load ApplicationConfig from FIT_CONNECT_CONFIG environment variable.
+     * Make sure this variable is set and points to a config.yaml.
+     *
+     * @return ApplicationConfig
+     * @throws FitConnectInitialisationException if the config file could not be loaded
+     */
+    public static ApplicationConfig loadConfigFromEnvironment() {
+        final Path configPath = getPathFromEnvironment();
+        return loadConfigFromPath(configPath);
+    }
+
+    /**
+     * Load ApplicationConfig from yaml string.
+     *
+     * @param configYaml string content of the yaml config file
+     * @return ApplicationConfig
+     */
+    public static ApplicationConfig loadConfigFromYamlString(final String configYaml) {
+        return mergeConfigEnvironmentsWithSdkDefaults(initNewYaml().loadAs(configYaml, ApplicationConfig.class));
+    }
+
+    /**
+     * Merge the configured default environments with the config from user.
+     *
+     * @param config loaded config with user overrides
+     * @return application config that contains default environments, overrides of the defaults  user specific environments
+     */
+    private static ApplicationConfig mergeConfigEnvironmentsWithSdkDefaults(final ApplicationConfig config) {
+
+        final Map<EnvironmentName, Environment> sdkEnvironments = DefaultEnvironments.getEnvironmentsAsMap();
+        final Map<EnvironmentName, Environment> configEnvironments = config.getEnvironments();
+
+        for (final EnvironmentName environmentName : configEnvironments.keySet()) {
+            final Environment configEnvironment = configEnvironments.get(environmentName);
+            if (sdkEnvironments.containsKey(environmentName)) {
+                sdkEnvironments.put(environmentName, configEnvironment.merge(sdkEnvironments.get(environmentName)));
+            } else {
+                sdkEnvironments.put(environmentName, configEnvironment);
+            }
+        }
+        return config.withEnvironments(sdkEnvironments);
+    }
+
+    private static Path getPathFromEnvironment() {
+        try {
+            return Path.of(System.getenv(CONFIG_ENV_KEY_NAME));
+        } catch (final NullPointerException | InvalidPathException e) {
+            LOGGER.warn("Environment variable {} could not be loaded", CONFIG_ENV_KEY_NAME);
+            throw new FitConnectInitialisationException(e.getMessage(), e);
+        }
+    }
+
+    private static Yaml initNewYaml() {
+        final Yaml yaml = new Yaml();
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        return yaml;
+    }
+}
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java b/client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ClientFactory.java
similarity index 88%
rename from client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
rename to client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ClientFactory.java
index eb522217da6bb1d45bfab35864a199890e477ae3..cd88d765d530ae7d069fee4bd087ba25165a2416 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/factory/ClientFactory.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/bootstrap/ClientFactory.java
@@ -1,14 +1,13 @@
-package dev.fitko.fitconnect.client.factory;
+package dev.fitko.fitconnect.client.bootstrap;
 
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
-import dev.fitko.fitconnect.api.config.BuildInfo;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.build.BuildInfo;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.config.SubscriberConfig;
 import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
@@ -22,7 +21,7 @@ import dev.fitko.fitconnect.api.services.routing.RoutingService;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import dev.fitko.fitconnect.api.services.submission.SubmissionService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
-import dev.fitko.fitconnect.client.RoutingClient;
+import dev.fitko.fitconnect.client.RouterClient;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.SubscriberClient;
 import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
@@ -57,7 +56,7 @@ import java.text.ParseException;
 import java.util.List;
 
 /**
- * Factory that constructs {@link SenderClient}, {@link SubscriberClient} and {@link RoutingClient}.
+ * Factory that constructs {@link SenderClient}, {@link SubscriberClient} and {@link RouterClient}.
  */
 public final class ClientFactory {
 
@@ -77,8 +76,9 @@ public final class ClientFactory {
      * Create a new {@link SenderClient} to send submissions that is automatically configured via a provided {@link ApplicationConfig}.
      *
      * @return the sender client
+     * @throws FitConnectInitialisationException on errors during the sender client construction
      */
-    public static SenderClient getSenderClient(final ApplicationConfig config) {
+    public static SenderClient getSenderClient(final ApplicationConfig config) throws FitConnectInitialisationException {
         checkNotNull(config);
         LOGGER.info("Initializing sender client ...");
         return new SenderClient(getSender(config, loadBuildInfo()));
@@ -88,8 +88,9 @@ public final class ClientFactory {
      * Create a new {@link SubscriberClient} to receive submissions that is automatically configured via a provided {@link ApplicationConfig}.
      *
      * @return the subscriber client
+     * @throws FitConnectInitialisationException on errors during the subscriber client construction
      */
-    public static SubscriberClient getSubscriberClient(final ApplicationConfig config) {
+    public static SubscriberClient getSubscriberClient(final ApplicationConfig config) throws FitConnectInitialisationException {
 
         checkNotNull(config);
 
@@ -106,15 +107,16 @@ public final class ClientFactory {
     }
 
     /**
-     * Create a new {@link RoutingClient} to find destinations and services that is automatically configured via a provided {@link ApplicationConfig}.
+     * Create a new {@link RouterClient} to find destinations and services that is automatically configured via a provided {@link ApplicationConfig}.
      *
      * @return the routing client
+     * @throws FitConnectInitialisationException on errors during the routing client construction
      */
-    public static RoutingClient getRoutingClient(final ApplicationConfig config) {
+    public static RouterClient getRouterClient(final ApplicationConfig config) throws FitConnectInitialisationException {
 
         checkNotNull(config);
 
-        LOGGER.info("Initializing routing client ...");
+        LOGGER.info("Initializing router client ...");
         final HttpClient httpClient = getHttpClient(config, loadBuildInfo());
         final SchemaProvider schemaProvider = getSchemaProvider(httpClient, config.getSubmissionDataSchemaPath());
         final OAuthService authService = getSenderConfiguredAuthService(config, httpClient);
@@ -127,7 +129,7 @@ public final class ClientFactory {
         final RouteVerifier routeVerifier = getRouteVerifier(keyService, validator);
         final RoutingService routingService = getRoutingService(config, httpClient);
 
-        return new RoutingClient(routingService, routeVerifier);
+        return new RouterClient(routingService, routeVerifier);
     }
 
     private static Sender getSender(final ApplicationConfig config, final BuildInfo buildInfo) {
@@ -242,7 +244,7 @@ public final class ClientFactory {
             return RSAKey.parse(JWK.parse(key).toJSONObject());
         } catch (final ParseException | NullPointerException e) {
             LOGGER.error("Could not parse key, please check environment (config.yml)");
-            throw new InvalidKeyException("Key could not be parsed", e);
+            throw new FitConnectInitialisationException("Key could not be parsed", e);
         }
     }
 
@@ -251,24 +253,28 @@ public final class ClientFactory {
             return Files.readString(Path.of(pathName));
         } catch (final IOException | NullPointerException e) {
             LOGGER.error("Could not read {} from path '{}'. Please provide a valid path in your config.yml", errorKey, pathName);
-            throw new InitializationException(e.getMessage(), e);
+            throw new FitConnectInitialisationException(e.getMessage(), e);
         }
     }
 
     private static BuildInfo loadBuildInfo() {
-        final InputStream resource = ClientFactory.class.getClassLoader().getResourceAsStream(BUILD_INFO_PATH);
-        return new Yaml().loadAs(resource, BuildInfo.class);
+        try {
+            final InputStream resource = ClientFactory.class.getClassLoader().getResourceAsStream(BUILD_INFO_PATH);
+            return new Yaml().loadAs(resource, BuildInfo.class);
+        } catch (final Exception e) {
+            throw new FitConnectInitialisationException("Build info could not be loaded", e);
+        }
     }
 
     private static void checkNotNull(final ApplicationConfig config) {
         if (config == null) {
-            throw new InitializationException("application config must not be null");
+            throw new FitConnectInitialisationException("application config must not be null");
         }
     }
 
     private static String getPrivateDecryptionKeyPathFromSubscriber(final SubscriberConfig subscriberConfig) {
         if (subscriberConfig.getPrivateDecryptionKeyPaths().size() != 1) {
-            throw new InitializationException("Currently only one configured private key per subscriber is allowed !");
+            throw new FitConnectInitialisationException("Currently only one configured private key per subscriber is allowed !");
         }
         final String keyPath = subscriberConfig.getPrivateDecryptionKeyPaths().get(0);
         LOGGER.info("Reading private decryption key from {}", keyPath);
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
index e1f19a66702c72ce2a572d2df27124044e888dbd..0464636b6863ecc604cfdf73294b5cab540c37c9 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandExecutor.java
@@ -2,7 +2,7 @@ package dev.fitko.fitconnect.client.cli;
 
 import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
-import dev.fitko.fitconnect.api.exceptions.BatchImportException;
+import dev.fitko.fitconnect.api.exceptions.internal.BatchImportException;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.SubscriberClient;
 import dev.fitko.fitconnect.client.cli.batch.BatchImporter;
@@ -114,7 +114,7 @@ class CommandExecutor {
 
         final SendableSubmission sendableSubmission = SendableSubmission.Builder()
                 .setDestination(sendSubmissionCommand.destinationId)
-                .setServiceType(sendSubmissionCommand.serviceName, sendSubmissionCommand.leikaKey)
+                .setServiceType(sendSubmissionCommand.leikaKey, sendSubmissionCommand.serviceName)
                 .setJsonData(getDataAsString(sendSubmissionCommand.data), sendSubmissionCommand.schemaUri)
                 .addAttachments(attachments)
                 .build();
@@ -126,7 +126,7 @@ class CommandExecutor {
 
         final SendableSubmission sendableSubmission = SendableSubmission.Builder()
                 .setDestination(sendSubmissionCommand.destinationId)
-                .setServiceType(sendSubmissionCommand.serviceName, sendSubmissionCommand.leikaKey)
+                .setServiceType(sendSubmissionCommand.leikaKey, sendSubmissionCommand.serviceName)
                 .setXmlData(getDataAsString(sendSubmissionCommand.data), sendSubmissionCommand.schemaUri)
                 .addAttachments(attachments)
                 .build();
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandLineRunner.java b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandLineRunner.java
index 8b0cd7ef434ad68e5cb76abd97ecf2be2e24a4c1..67ebdb904577f552a49f3fad218db4f48d1dadcb 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandLineRunner.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/cli/CommandLineRunner.java
@@ -2,8 +2,8 @@ package dev.fitko.fitconnect.client.cli;
 
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.client.SenderClient;
-import dev.fitko.fitconnect.client.factory.ApplicationConfigLoader;
-import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.bootstrap.ApplicationConfigLoader;
+import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,7 +32,7 @@ public final class CommandLineRunner {
         try {
             return ApplicationConfigLoader.loadConfigFromEnvironment();
         } catch (final Exception e) {
-            LOGGER.warn("Could not load config from environment, loading default config {} {}", DEFAULT_CONFIG_NAME, e);
+            LOGGER.warn("Could not load config from environment, loading default config {}", DEFAULT_CONFIG_NAME);
             return ApplicationConfigLoader.loadConfigFromPath(Path.of(DEFAULT_CONFIG_NAME));
         }
     }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/cli/batch/CsvImporter.java b/client/src/main/java/dev/fitko/fitconnect/client/cli/batch/CsvImporter.java
index 6e48c08a892ec624417c97743f95838736d76930..2c6188981946374f749061f926951ca5da3ae9f2 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/cli/batch/CsvImporter.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/cli/batch/CsvImporter.java
@@ -2,7 +2,7 @@ package dev.fitko.fitconnect.client.cli.batch;
 
 import com.opencsv.bean.CsvToBeanBuilder;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
-import dev.fitko.fitconnect.api.exceptions.BatchImportException;
+import dev.fitko.fitconnect.api.exceptions.internal.BatchImportException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoader.java b/client/src/main/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoader.java
deleted file mode 100644
index 1bb043e8cc0d312f3fb5f1320e90808f5cdf6b28..0000000000000000000000000000000000000000
--- a/client/src/main/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoader.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package dev.fitko.fitconnect.client.factory;
-
-import dev.fitko.fitconnect.api.config.ApplicationConfig;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.Yaml;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-
-public final class ApplicationConfigLoader {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfigLoader.class);
-    private static final String CONFIG_ENV_KEY_NAME = "FIT_CONNECT_CONFIG";
-
-    private ApplicationConfigLoader() {
-    }
-
-    /**
-     * Load ApplicationConfig from path.
-     *
-     * @param configPath path to config file
-     * @return ApplicationConfig
-     * @throws InitializationException if the config file could not be loaded
-     */
-    public static ApplicationConfig loadConfigFromPath(final Path configPath) {
-        try {
-            return loadConfigFromYamlString(Files.readString(configPath));
-        } catch (final IOException | NullPointerException e) {
-            LOGGER.error("config file could not be loaded from path {}", configPath);
-            throw new InitializationException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Load ApplicationConfig from FIT_CONNECT_CONFIG environment variable.
-     * Make sure this variable is set and points to a config.yaml.
-     *
-     * @return ApplicationConfig
-     * @throws InitializationException if the config file could not be loaded
-     */
-    public static ApplicationConfig loadConfigFromEnvironment() {
-        final Path configPath = getPathFromEnvironment();
-        return loadConfigFromPath(configPath);
-    }
-
-    /**
-     * Load ApplicationConfig from yaml string.
-     *
-     * @param configYaml string content of the yaml config file
-     * @return ApplicationConfig
-     */
-    public static ApplicationConfig loadConfigFromYamlString(final String configYaml) {
-        return new Yaml().loadAs(configYaml, ApplicationConfig.class);
-    }
-
-    private static Path getPathFromEnvironment() {
-        try {
-            return Path.of(System.getenv(CONFIG_ENV_KEY_NAME));
-        } catch (final NullPointerException | InvalidPathException e) {
-            LOGGER.error("Environment variable {} could not be loaded", CONFIG_ENV_KEY_NAME);
-            throw new InitializationException(e.getMessage(), e);
-        }
-    }
-
-}
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/router/DestinationSearch.java b/client/src/main/java/dev/fitko/fitconnect/client/router/DestinationSearch.java
index eab9b403648844cbf232e5d7b68ae78acfc177ef..a8b6d3a512b8b23c998f4e858fc10590fcfeb0a3 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/router/DestinationSearch.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/router/DestinationSearch.java
@@ -1,5 +1,7 @@
 package dev.fitko.fitconnect.client.router;
 
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectRouterException;
+import dev.fitko.fitconnect.client.RouterClient;
 import lombok.AllArgsConstructor;
 import lombok.Value;
 
@@ -33,9 +35,9 @@ public class DestinationSearch {
          *
          * @param leikaKey service identifier
          * @return Builder
-         * @throws IllegalArgumentException if the leika key pattern is not matching
+         * @throws FitConnectRouterException if the leika key pattern is not matching
          */
-        OptionalProperties withLeikaKey(final String leikaKey) throws IllegalArgumentException;
+        OptionalProperties withLeikaKey(final String leikaKey) throws FitConnectRouterException;
     }
 
     public interface OptionalProperties {
@@ -45,53 +47,53 @@ public class DestinationSearch {
          *
          * @param ars amtlicher regionalschlüssel
          * @return Builder
-         * @throws IllegalArgumentException if the ars key pattern is not matching
+         * @throws FitConnectRouterException if the ars key pattern is not matching
          */
-        Pagination withArs(final String ars) throws IllegalArgumentException;
+        Pagination withArs(final String ars) throws FitConnectRouterException;
 
         /**
          * Official regional key of the area.
          *
          * @param ags amtlicher gemeindeschlüssel
          * @return Builder
-         * @throws IllegalArgumentException if the ags key pattern is not matching
+         * @throws FitConnectRouterException if the ags key pattern is not matching
          */
-        Pagination withAgs(final String ags) throws IllegalArgumentException;
+        Pagination withAgs(final String ags) throws FitConnectRouterException;
 
         /**
          * ID of the area. This ID can be determined via the routing clients <code>findAreas</code> search.
          *
          * @param areaId id of the area
          * @return Builder
-         * @throws IllegalArgumentException if the area id pattern is not matching
-         * @see dev.fitko.fitconnect.client.RoutingClient#findAreas(String, int, int)
+         * @throws FitConnectRouterException if the area id pattern is not matching
+         * @see RouterClient#findAreas(String, int, int)
          */
-        Pagination withAreaId(final String areaId) throws IllegalArgumentException;
+        Pagination withAreaId(final String areaId) throws FitConnectRouterException;
 
         /**
          * Start position of the subset of the result set. Default is 0.
          *
          * @param offset start of the subset
          * @return Builder
-         * @throws IllegalArgumentException if the offset is a negative number
+         * @throws FitConnectRouterException if the offset is a negative number
          */
-        Builder withOffset(final int offset) throws IllegalArgumentException;
+        Builder withOffset(final int offset) throws FitConnectRouterException;
 
         /**
          * Max. size of the subset of the result set. Maximum is 500. Default is 100.
          *
          * @param limit max. entries in the subset
          * @return Builder
-         * @throws IllegalArgumentException if the limit is > 500
+         * @throws FitConnectRouterException if the limit is > 500
          */
-        Builder withLimit(final int limit) throws IllegalArgumentException;
+        Builder withLimit(final int limit) throws FitConnectRouterException;
 
         /**
          * Construct the search request.
          *
          * @return DestinationSearch
          */
-        DestinationSearch build() throws IllegalArgumentException;
+        DestinationSearch build() throws FitConnectRouterException;
 
     }
 
@@ -102,25 +104,25 @@ public class DestinationSearch {
          *
          * @param offset start of the subset
          * @return Builder
-         * @throws IllegalArgumentException if the offset is a negative number
+         * @throws FitConnectRouterException if the offset is a negative number
          */
-        Pagination withOffset(final int offset) throws IllegalArgumentException;
+        Pagination withOffset(final int offset) throws FitConnectRouterException;
 
         /**
          * Max. size of the subset of the result set. Maximum is 500. Default is 100.
          *
          * @param limit max. entries in the subset
          * @return Builder
-         * @throws IllegalArgumentException if the limit is > 500
+         * @throws FitConnectRouterException if the limit is > 500
          */
-        Pagination withLimit(final int limit) throws IllegalArgumentException;
+        Pagination withLimit(final int limit) throws FitConnectRouterException;
 
         /**
          * Construct the search request.
          *
          * @return DestinationSearch
          */
-        DestinationSearch build() throws IllegalArgumentException;
+        DestinationSearch build() throws FitConnectRouterException;
 
     }
 
@@ -130,7 +132,7 @@ public class DestinationSearch {
          *
          * @return DestinationSearch
          */
-        DestinationSearch build() throws IllegalArgumentException;
+        DestinationSearch build() throws FitConnectRouterException;
     }
 
     private static class Builder implements MandatoryProperties, OptionalProperties, Pagination, BuildSearch {
@@ -149,18 +151,18 @@ public class DestinationSearch {
 
 
         @Override
-        public OptionalProperties withLeikaKey(final String leikaKey) throws IllegalArgumentException {
+        public OptionalProperties withLeikaKey(final String leikaKey) throws FitConnectRouterException {
             if (!LEIKA_KEY_PATTERN.matcher(leikaKey).matches()) {
-                throw new IllegalArgumentException("Leika key does not match allowed pattern " + LEIKA_KEY_PATTERN);
+                throw new FitConnectRouterException("Leika key does not match allowed pattern " + LEIKA_KEY_PATTERN);
             }
             this.leikaKey = leikaKey;
             return this;
         }
 
         @Override
-        public Pagination withArs(final String ars) throws IllegalArgumentException {
+        public Pagination withArs(final String ars) throws FitConnectRouterException {
             if (!ARS_PATTERN.matcher(ars).matches()) {
-                throw new IllegalArgumentException("ARS key does not match allowed pattern " + ARS_PATTERN);
+                throw new FitConnectRouterException("ARS key does not match allowed pattern " + ARS_PATTERN);
             }
             this.ars = ars;
 
@@ -168,55 +170,48 @@ public class DestinationSearch {
         }
 
         @Override
-        public Pagination withAgs(final String ags) throws IllegalArgumentException {
+        public Pagination withAgs(final String ags) throws FitConnectRouterException {
             if (!AGS_PATTERN.matcher(ags).matches()) {
-                throw new IllegalArgumentException("AGS key does not match allowed pattern " + AGS_PATTERN);
+                throw new FitConnectRouterException("AGS key does not match allowed pattern " + AGS_PATTERN);
             }
             this.ags = ags;
             return this;
         }
 
-        /**
-         * ID of the area. This ID can be determined via the routing clients <code>findAreas</code> search.
-         *
-         * @param areaId id of the area
-         * @return Builder
-         * @throws IllegalArgumentException if the area id pattern is not matching
-         * @see dev.fitko.fitconnect.client.RoutingClient#findAreas(String, int, int)
-         */
-        public Pagination withAreaId(final String areaId) throws IllegalArgumentException {
+        @Override
+        public Pagination withAreaId(final String areaId) throws FitConnectRouterException {
             if (!AREA_ID_PATTERN.matcher(areaId).matches()) {
-                throw new IllegalArgumentException("AreaId key does not match allowed pattern " + AREA_ID_PATTERN);
+                throw new FitConnectRouterException("AreaId key does not match allowed pattern " + AREA_ID_PATTERN);
             }
             this.areaId = areaId;
             return this;
         }
 
         @Override
-        public Builder withOffset(final int offset) throws IllegalArgumentException {
+        public Builder withOffset(final int offset) throws FitConnectRouterException {
             if (limit < 0) {
-                throw new IllegalArgumentException("offset must be positive");
+                throw new FitConnectRouterException("offset must be positive");
             }
             this.offset = offset;
             return this;
         }
 
         @Override
-        public Builder withLimit(final int limit) throws IllegalArgumentException {
+        public Builder withLimit(final int limit) throws FitConnectRouterException {
             if (limit > 500) {
-                throw new IllegalArgumentException("limit must no be > 500");
+                throw new FitConnectRouterException("limit must no be > 500");
             }
             this.limit = limit;
             return this;
         }
 
         @Override
-        public DestinationSearch build() throws IllegalArgumentException {
+        public DestinationSearch build() throws FitConnectRouterException {
             if (leikaKey == null) {
-                throw new IllegalArgumentException("leikaKey is mandatory");
+                throw new FitConnectRouterException("leikaKey is mandatory");
             }
             if(Arrays.asList(areaId, ags, ars).stream().allMatch(Objects::isNull)){
-                throw new IllegalArgumentException("at least one regional search criterion (areaId, ars or ags) is mandatory");
+                throw new FitConnectRouterException("at least one regional search criterion (areaId, ars or ags) is mandatory");
             }
             return new DestinationSearch(leikaKey, ars, ags, areaId, offset, limit);
         }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
index 932fe4ae7c3dc4e673997642dc045eeca63cbe91..1a4c4ba24269fbec3e9709d3a95f60a8bde1004b 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/model/Attachment.java
@@ -1,6 +1,6 @@
 package dev.fitko.fitconnect.client.sender.model;
 
-import dev.fitko.fitconnect.api.exceptions.AttachmentCreationException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSenderException;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
@@ -23,7 +23,7 @@ public class Attachment {
      * @param filePath path of the attachment file
      * @param mimeType mime-type of the attachment
      */
-    public static Attachment fromPath(final Path filePath, final String mimeType) throws AttachmentCreationException {
+    public static Attachment fromPath(final Path filePath, final String mimeType) throws FitConnectSenderException {
         return fromPath(filePath, mimeType, null, null);
     }
 
@@ -34,9 +34,9 @@ public class Attachment {
      * @param mimeType    mime-type of the attachment
      * @param fileName    name of the attachment file
      * @param description description of the attachment file
-     * @throws AttachmentCreationException if the file path could not be read
+     * @throws FitConnectSenderException if the file path could not be read
      */
-    public static Attachment fromPath(final Path filePath, final String mimeType, final String fileName, final String description) throws AttachmentCreationException {
+    public static Attachment fromPath(final Path filePath, final String mimeType, final String fileName, final String description) throws FitConnectSenderException {
         return new Attachment(readBytesFromPath(filePath), mimeType, fileName, description);
     }
 
@@ -45,9 +45,9 @@ public class Attachment {
      *
      * @param inputStream stream of the attachment data
      * @param mimeType    mime type of the provided attachment data
-     * @throws AttachmentCreationException if the input-stream could not be read
+     * @throws FitConnectSenderException if the input-stream could not be read
      */
-    public static Attachment fromInputStream(final InputStream inputStream, final String mimeType) throws AttachmentCreationException {
+    public static Attachment fromInputStream(final InputStream inputStream, final String mimeType) throws FitConnectSenderException {
         return fromInputStream(inputStream, mimeType, null, null);
     }
 
@@ -58,9 +58,9 @@ public class Attachment {
      * @param mimeType    mime type of the provided attachment data
      * @param fileName    name of the attachment file
      * @param description description of the attachment file
-     * @throws AttachmentCreationException if the input-stream could not be read
+     * @throws FitConnectSenderException if the input-stream could not be read
      */
-    public static Attachment fromInputStream(final InputStream inputStream, final String mimeType, final String fileName, final String description) throws AttachmentCreationException {
+    public static Attachment fromInputStream(final InputStream inputStream, final String mimeType, final String fileName, final String description) throws FitConnectSenderException {
         return new Attachment(readBytesFromInputStream(inputStream), mimeType, fileName, description);
     }
 
@@ -173,7 +173,7 @@ public class Attachment {
         try (final BufferedInputStream bis = new BufferedInputStream(inputStream)) {
             return bis.readAllBytes();
         } catch (final IOException e) {
-            throw new AttachmentCreationException("Attachment could not be read from input-stream ", e);
+            throw new FitConnectSenderException("Attachment could not be read from input-stream ", e);
         }
     }
 
@@ -181,7 +181,7 @@ public class Attachment {
         try {
             return Files.readAllBytes(path);
         } catch (final IOException e) {
-            throw new AttachmentCreationException("Reading attachment from path '" + path + "' failed ", e);
+            throw new FitConnectSenderException("Reading attachment from path '" + path + "' failed ", e);
         }
     }
 
@@ -189,7 +189,7 @@ public class Attachment {
         try {
             return Path.of(fileName).getFileName().toString();
         } catch (final InvalidPathException e) {
-            throw new AttachmentCreationException("Reading filename '" + fileName + "' failed ", e);
+            throw new FitConnectSenderException("Reading filename '" + fileName + "' failed ", e);
         }
     }
 
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedAttachmentsStep.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedAttachmentsStep.java
index 0663fe0b801ecc5c3b5410135bf226d0fdb1ee6a..18efe2857be1ba65402eb15070c6a2ad43a7b48c 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedAttachmentsStep.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedAttachmentsStep.java
@@ -25,5 +25,9 @@ public interface EncryptedAttachmentsStep {
      */
     EncryptedAttachmentsStep addEncryptedAttachment(UUID id, String content);
 
+    /**
+     * Constructs a new sendable encrypted submission from the data that is set.
+     * @return SendableEncryptedSubmission
+     */
     SendableEncryptedSubmission build();
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedBuildStep.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedBuildStep.java
index 81cf0ffb415443be0f4e2ad1e09471507e060ca4..450d9bd3e890c92522037c32cc37bc3d0e853e87 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedBuildStep.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/encrypted/EncryptedBuildStep.java
@@ -4,5 +4,9 @@ import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
 
 public interface EncryptedBuildStep {
 
+    /**
+     * Constructs a new sendable encrypted submission from the data that is set.
+     * @return SendableEncryptedSubmission
+     */
     SendableEncryptedSubmission build();
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/BuildStep.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/BuildStep.java
index 5ac27408c87c9970947ede5e74333e378dd5614f..8577afd40e2ab449b5fc61d36b2480f4fc6a137c 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/BuildStep.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/BuildStep.java
@@ -4,5 +4,9 @@ import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
 
 public interface BuildStep {
 
+    /**
+     * Constructs a new sendable submission from the data that is set.
+     * @return SendableSubmission
+     */
     SendableSubmission build();
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/OptionalPropertiesStep.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/OptionalPropertiesStep.java
index 014d8e47bff914d8a17465bd96f2522d9332cd42..f1032d082681e35e0fbd4db4ec3adb8b4ef85d46 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/OptionalPropertiesStep.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/steps/unencrypted/OptionalPropertiesStep.java
@@ -44,5 +44,9 @@ public interface OptionalPropertiesStep {
 
     OptionalPropertiesStep setPaymentInformation(PaymentInformation paymentInformation);
 
+    /**
+     * Constructs a new sendable submission from the data that is set.
+     * @return SendableSubmission
+     */
     SendableSubmission build();
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendEncryptedSubmissionStrategy.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendEncryptedSubmissionStrategy.java
index 41e2c405e7f1ac1b78a2fbcd1b8d934bbfe2f79d..0b9044b5ac53c75a76f1ea88482ad0ca86ff946f 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendEncryptedSubmissionStrategy.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendEncryptedSubmissionStrategy.java
@@ -5,8 +5,6 @@ import dev.fitko.fitconnect.api.domain.model.submission.CreateSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionNotCreatedException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
 import dev.fitko.fitconnect.core.util.StopWatch;
@@ -33,34 +31,25 @@ public class SendEncryptedSubmissionStrategy {
 
     public SentSubmission send(final SendableEncryptedSubmission submissionPayload) {
 
-        try {
+        final Map<UUID, String> encryptedAttachments = submissionPayload.getAttachments();
+        final String encryptedData = submissionPayload.getData();
+        final String encryptedMetadata = submissionPayload.getMetadata();
 
-            final Map<UUID, String> encryptedAttachments = submissionPayload.getAttachments();
-            final String encryptedData = submissionPayload.getData();
-            final String encryptedMetadata = submissionPayload.getMetadata();
+        final UUID submissionId = announceNewSubmission(submissionPayload, encryptedAttachments);
+        final SubmitSubmission submitSubmission = buildSubmitSubmission(submissionId, encryptedData, encryptedMetadata);
 
-            final UUID submissionId = announceNewSubmission(submissionPayload, encryptedAttachments);
-            final SubmitSubmission submitSubmission = buildSubmitSubmission(submissionId, encryptedData, encryptedMetadata);
+        final var startTimeAttachmentUpload = StopWatch.start();
+        uploadAttachments(encryptedAttachments, submissionId);
+        LOGGER.info("Uploading attachments took {}", StopWatch.stopWithFormattedTime(startTimeAttachmentUpload));
 
-            final var startTimeAttachmentUpload = StopWatch.start();
-            uploadAttachments(encryptedAttachments, submissionId);
-            LOGGER.info("Uploading attachments took {}", StopWatch.stopWithFormattedTime(startTimeAttachmentUpload));
+        final var startTimeSubmissionUpload = StopWatch.start();
+        final Submission submission = sender.sendSubmission(submitSubmission);
+        LOGGER.info("Uploading submission took {}", StopWatch.stopWithFormattedTime(startTimeSubmissionUpload));
 
-            final var startTimeSubmissionUpload = StopWatch.start();
-            final Submission submission = sender.sendSubmission(submitSubmission);
-            LOGGER.info("Uploading submission took {}", StopWatch.stopWithFormattedTime(startTimeSubmissionUpload));
+        LOGGER.info("SUCCESSFULLY HANDED IN SUBMISSION ! \n");
+        final AuthenticationTags authenticationTags = buildAuthenticationTagsForEncryptedSubmission(encryptedAttachments, encryptedData, encryptedMetadata);
+        return buildSentSubmission(authenticationTags, submission);
 
-            LOGGER.info("SUCCESSFULLY HANDED IN SUBMISSION ! \n");
-            final AuthenticationTags authenticationTags = buildAuthenticationTagsForEncryptedSubmission(encryptedAttachments, encryptedData, encryptedMetadata);
-            return buildSentSubmission(authenticationTags, submission);
-
-        } catch (final RestApiException e) {
-            LOGGER.error("Sending submission failed", e);
-        } catch (final SubmissionNotCreatedException e) {
-            LOGGER.error("Failed to announce new submission", e);
-        }
-
-        return null;
     }
 
     private UUID announceNewSubmission(final SendableEncryptedSubmission submissionPayload, final Map<UUID, String> encryptedAttachments) {
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
index e36adbc7be91a6499c7c2c8f1c2527d6c4ec3437..b1af51fa937b7a6ba67dbf85462c4edd0704d6c4 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/sender/strategies/SendNewSubmissionStrategy.java
@@ -1,8 +1,6 @@
 package dev.fitko.fitconnect.client.sender.strategies;
 
 import com.nimbusds.jose.jwk.RSAKey;
-import dev.fitko.fitconnect.api.domain.model.destination.Destination;
-import dev.fitko.fitconnect.api.domain.model.destination.DestinationService;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.metadata.ContentStructure;
 import dev.fitko.fitconnect.api.domain.model.metadata.Hash;
@@ -18,13 +16,6 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.AttachmentCreationException;
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
-import dev.fitko.fitconnect.api.exceptions.KeyNotRetrievedException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionNotCreatedException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.AttachmentPayload;
@@ -35,14 +26,16 @@ import org.slf4j.LoggerFactory;
 
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
-import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
-import static dev.fitko.fitconnect.api.config.SchemaConfig.METADATA_V_1_0_0;
-import static dev.fitko.fitconnect.client.util.SubmissionUtil.*;
+import static dev.fitko.fitconnect.api.config.defaults.SchemaConfig.METADATA_V_1_0_0;
+import static dev.fitko.fitconnect.client.util.SubmissionUtil.buildAuthenticationTags;
+import static dev.fitko.fitconnect.client.util.SubmissionUtil.buildCreateSubmission;
+import static dev.fitko.fitconnect.client.util.SubmissionUtil.buildSentSubmission;
+import static dev.fitko.fitconnect.client.util.SubmissionUtil.buildSubmitSubmission;
 
 public class SendNewSubmissionStrategy {
 
@@ -57,70 +50,48 @@ public class SendNewSubmissionStrategy {
     public SentSubmission send(final SendableSubmission sendableSubmission) {
 
         final UUID destinationId = sendableSubmission.getDestinationId();
+        final RSAKey encryptionKey = sender.getEncryptionKeyForDestination(destinationId);
 
-        try {
-
-            final RSAKey encryptionKey = sender.getEncryptionKeyForDestination(destinationId);
-            final Destination destination = sender.getDestination(destinationId);
-
-            LOGGER.info("Encrypting attachments ...");
-            final List<AttachmentPayload> encryptedAttachments = encryptAndHashAttachments(encryptionKey, sendableSubmission.getAttachments());
-            final CreateSubmission newSubmission = buildCreateSubmission(sendableSubmission, encryptedAttachments);
-
-            final SubmissionForPickup announcedSubmission = sender.createSubmission(newSubmission);
-            final UUID announcedSubmissionId = announcedSubmission.getSubmissionId();
-
-            final var startTimeAttachmentUpload = StopWatch.start();
-            uploadAttachments(encryptedAttachments, announcedSubmissionId);
-            LOGGER.info("Uploading attachments took {}", StopWatch.stopWithFormattedTime(startTimeAttachmentUpload));
-
-            LOGGER.info("Creating metadata");
-            final Metadata metadata = buildMetadata(sendableSubmission, destination, encryptedAttachments);
-
-            final ValidationResult validatedMetadata = sender.validateMetadata(metadata);
-            if (validatedMetadata.hasError()) {
-                LOGGER.error("Metadata does not match schema", validatedMetadata.getError());
-                return null;
-            }
-
-            LOGGER.info("Encrypting submission data ...");
-            final String encryptedData = sender.encryptBytes(encryptionKey, sendableSubmission.getData().getBytes(StandardCharsets.UTF_8));
-
-            LOGGER.info("Encrypting metadata ...");
-            final String encryptedMetadata = sender.encryptObject(encryptionKey, metadata);
-
-            final var startTimeSubmissionUpload = StopWatch.start();
-            final Submission submission = sender.sendSubmission(buildSubmitSubmission(announcedSubmissionId, encryptedData, encryptedMetadata));
-            LOGGER.info("Uploading submission took {}", StopWatch.stopWithFormattedTime(startTimeSubmissionUpload));
-
-            LOGGER.info("SUCCESSFULLY HANDED IN SUBMISSION ! \n");
-            final AuthenticationTags authenticationTags = buildAuthenticationTags(encryptedAttachments, encryptedData, encryptedMetadata);
-            return buildSentSubmission(authenticationTags, submission);
-
-        } catch (final EncryptionException e) {
-            LOGGER.error("Encrypting submission failed", e);
-        } catch (final RestApiException e) {
-            LOGGER.error("Sending submission failed", e);
-        } catch (final SchemaNotFoundException e) {
-            LOGGER.error("Required schema to send valid submission not found", e);
-        } catch (final SubmissionNotCreatedException e) {
-            LOGGER.error("Failed to announce new submission", e);
-        } catch (final KeyNotRetrievedException e) {
-            LOGGER.error("Getting encryption key for destination {} failed", destinationId, e);
-        } catch (final InvalidKeyException e) {
-            LOGGER.error("Key for destination {} is invalid", destinationId, e);
-        } catch (final AttachmentCreationException e) {
-            LOGGER.error("Reading file failed. Attachment will not be created.", e);
+        LOGGER.info("Encrypting attachments ...");
+        final List<AttachmentPayload> encryptedAttachments = encryptAndHashAttachments(encryptionKey, sendableSubmission.getAttachments());
+        final CreateSubmission newSubmission = buildCreateSubmission(sendableSubmission, encryptedAttachments);
+
+        final SubmissionForPickup announcedSubmission = sender.createSubmission(newSubmission);
+        final UUID announcedSubmissionId = announcedSubmission.getSubmissionId();
+
+        final var startTimeAttachmentUpload = StopWatch.start();
+        uploadAttachments(encryptedAttachments, announcedSubmissionId);
+        LOGGER.info("Uploading attachments took {}", StopWatch.stopWithFormattedTime(startTimeAttachmentUpload));
+
+        LOGGER.info("Creating metadata");
+        final Metadata metadata = buildMetadata(sendableSubmission, encryptedAttachments);
+
+        final ValidationResult validatedMetadata = sender.validateMetadata(metadata);
+        if (validatedMetadata.hasError()) {
+            LOGGER.error("Metadata does not match schema", validatedMetadata.getError());
+            return null;
         }
 
-        return null;
+        LOGGER.info("Encrypting submission data ...");
+        final String encryptedData = sender.encryptBytes(encryptionKey, sendableSubmission.getData().getBytes(StandardCharsets.UTF_8));
+
+        LOGGER.info("Encrypting metadata ...");
+        final String encryptedMetadata = sender.encryptObject(encryptionKey, metadata);
+
+        final var startTimeSubmissionUpload = StopWatch.start();
+        final Submission submission = sender.sendSubmission(buildSubmitSubmission(announcedSubmissionId, encryptedData, encryptedMetadata));
+        LOGGER.info("Uploading submission took {}", StopWatch.stopWithFormattedTime(startTimeSubmissionUpload));
+
+        LOGGER.info("SUCCESSFULLY HANDED IN SUBMISSION ! \n");
+        final AuthenticationTags authenticationTags = buildAuthenticationTags(encryptedAttachments, encryptedData, encryptedMetadata);
+        return buildSentSubmission(authenticationTags, submission);
+
     }
 
-    private Metadata buildMetadata(final SendableSubmission sendableSubmission, final Destination destination, final List<AttachmentPayload> encryptedAttachments) {
+    private Metadata buildMetadata(final SendableSubmission sendableSubmission, final List<AttachmentPayload> encryptedAttachments) {
         final String hashedData = sender.createHash(sendableSubmission.getData().getBytes(StandardCharsets.UTF_8));
 
         final String dataMimeType = sendableSubmission.getDataMimeType();
-        final URI schemaUri = getSubmissionSchemaFromDestination(destination, dataMimeType);
         final Data data = createData(dataMimeType, hashedData, sendableSubmission.getSchemaUri());
         final List<ApiAttachment> attachmentMetadata = encryptedAttachments.stream().map(this::toHashedAttachment).collect(Collectors.toList());
 
@@ -138,13 +109,13 @@ public class SendNewSubmissionStrategy {
     }
 
     private void setOptionalProperties(final SendableSubmission sendableSubmission, final Metadata metadata) {
-        if(sendableSubmission.getAuthenticationInformation() != null){
+        if (sendableSubmission.getAuthenticationInformation() != null) {
             metadata.setAuthenticationInformation(sendableSubmission.getAuthenticationInformation());
         }
-        if(sendableSubmission.getReplyChannel() != null){
+        if (sendableSubmission.getReplyChannel() != null) {
             metadata.setReplyChannel(sendableSubmission.getReplyChannel());
         }
-        if(sendableSubmission.getPaymentInformation() != null){
+        if (sendableSubmission.getPaymentInformation() != null) {
             metadata.setPaymentInformation(sendableSubmission.getPaymentInformation());
         }
     }
@@ -179,16 +150,6 @@ public class SendNewSubmissionStrategy {
         return attachment;
     }
 
-    private URI getSubmissionSchemaFromDestination(final Destination destination, final String mimeType) {
-        return destination.getServices().stream()
-                .map(DestinationService::getSubmissionSchemas)
-                .flatMap(Collection::stream)
-                .filter(schema -> schema.getMimeType().value().equals(mimeType))
-                .map(SubmissionSchema::getSchemaUri)
-                .findFirst()
-                .orElseThrow(() -> new SchemaNotFoundException("Destination does not support data with mime-type " + mimeType));
-    }
-
     private void uploadAttachments(final List<AttachmentPayload> attachmentPayloads, final UUID submissionId) {
         if (attachmentPayloads.isEmpty()) {
             LOGGER.info("No attachments to upload");
@@ -206,19 +167,15 @@ public class SendNewSubmissionStrategy {
     }
 
     private AttachmentPayload encryptAndHashAttachment(final RSAKey encryptionKey, final Attachment attachment) {
-        try {
-            final String encryptedAttachment = sender.encryptBytes(encryptionKey, attachment.getDataAsBytes());
-            final String hashedBytes = sender.createHash(attachment.getDataAsBytes());
-            return AttachmentPayload.builder()
-                    .attachmentId(UUID.randomUUID())
-                    .hashedData(hashedBytes)
-                    .encryptedData(encryptedAttachment)
-                    .mimeType(attachment.getMimeType())
-                    .fileName(attachment.getFileName())
-                    .description(attachment.getDescription())
-                    .build();
-        } catch (final EncryptionException e) {
-            throw new AttachmentCreationException("Attachment '" + attachment.getFileName() + "' could not be encrypted ", e);
-        }
+        final String encryptedAttachment = sender.encryptBytes(encryptionKey, attachment.getDataAsBytes());
+        final String hashedBytes = sender.createHash(attachment.getDataAsBytes());
+        return AttachmentPayload.builder()
+                .attachmentId(UUID.randomUUID())
+                .hashedData(hashedBytes)
+                .encryptedData(encryptedAttachment)
+                .mimeType(attachment.getMimeType())
+                .fileName(attachment.getFileName())
+                .description(attachment.getDescription())
+                .build();
     }
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/subscriber/ReceivedSubmission.java b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/ReceivedSubmission.java
index af264014b0c38f64960aba24113d38c44c09cf19..f89507bcb6b0df0463625f145dea625c7693040d 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/subscriber/ReceivedSubmission.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/ReceivedSubmission.java
@@ -5,6 +5,7 @@ import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSubscriberException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
@@ -14,6 +15,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import static dev.fitko.fitconnect.api.domain.model.event.EventPayload.*;
+
 public class ReceivedSubmission {
 
     private final transient Subscriber subscriber;
@@ -40,7 +43,11 @@ public class ReceivedSubmission {
      * @see <a href="https://docs.fitko.de/fit-connect/docs/getting-started/event-log/events">FIT-Connect Events</a>
      */
     public void acceptSubmission(final Problem... problems) {
-        subscriber.acceptSubmission(EventPayload.forAcceptEventWithAttachments(submission, encryptedAttachments, problems));
+        try {
+            subscriber.acceptSubmission(getEventPayload(problems));
+        } catch (final Exception e) {
+            throw new FitConnectSubscriberException("Accepting submission failed", e.getCause() != null ? e.getCause() : e);
+        }
     }
 
     /**
@@ -50,7 +57,11 @@ public class ReceivedSubmission {
      * @see <a href="https://docs.fitko.de/fit-connect/docs/getting-started/event-log/events">FIT-Connect Events</a>
      */
     public void rejectSubmission(final List<Problem> rejectionProblems) {
-        subscriber.rejectSubmission(EventPayload.forRejectEvent(submission, rejectionProblems));
+        try {
+            subscriber.rejectSubmission(forRejectEvent(submission, rejectionProblems));
+        } catch (final Exception e) {
+            throw new FitConnectSubscriberException("Rejecting submission failed", e.getCause() != null ? e.getCause() : e);
+        }
     }
 
     /**
@@ -125,4 +136,8 @@ public class ReceivedSubmission {
         return submission.getSubmissionId();
     }
 
+    private EventPayload getEventPayload(final Problem[] problems) {
+        return attachments.isEmpty() ? forAcceptEvent(submission, problems) : forAcceptEventWithAttachments(submission, encryptedAttachments, problems);
+    }
+
 }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java
index ab00bfff2f4e3c136ae98a771c5a07473b1a26bb..c86b38e662c1d8bfd24ce04d9aae83bb159600a1 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/subscriber/SubmissionReceiver.java
@@ -22,12 +22,12 @@ import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForVa
 import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
-import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.AuthenticationTagsEmptyException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.SubmissionRequestException;
+import dev.fitko.fitconnect.api.exceptions.internal.SubmitEventNotFoundException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
@@ -245,7 +245,7 @@ public class SubmissionReceiver {
     }
 
     private void reject(final Submission submission, final List<Problem> problems) {
-        if (config.isEnableAutoReject()) {
+        if (config.isAutoRejectEnabled()) {
             subscriber.rejectSubmission(EventPayload.forRejectEvent(submission, problems));
         }
     }
diff --git a/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java b/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
index aabf37acdf8163927080dec66188026cb997aa7f..3f0bac9e7471edb68f392be3e3771570a76f8ec4 100644
--- a/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
+++ b/client/src/main/java/dev/fitko/fitconnect/client/util/ValidDataGuard.java
@@ -27,7 +27,6 @@ public class ValidDataGuard {
      *
      * @param sendableEncryptedSubmission payload to be checked
      * @throws IllegalArgumentException if one of the checks fails
-     * @throws IllegalStateException    if the destination does not have a declared service type
      */
     public void ensureValidDataPayload(final SendableEncryptedSubmission sendableEncryptedSubmission) {
         if (sendableEncryptedSubmission == null) {
@@ -39,7 +38,7 @@ public class ValidDataGuard {
         if (sendableEncryptedSubmission.getMetadata() == null) {
             throw new IllegalArgumentException("Encrypted metadata must not be null.");
         }
-        testDefaults(sendableEncryptedSubmission.getDestinationId(), sendableEncryptedSubmission.getServiceName(), sendableEncryptedSubmission.getServiceIdentifier());
+        testDefaults(sendableEncryptedSubmission.getDestinationId(), sendableEncryptedSubmission.getServiceIdentifier());
     }
 
     /**
@@ -59,7 +58,7 @@ public class ValidDataGuard {
         testDefaults(sendableSubmission);
     }
 
-    private void testDefaults(final UUID destinationId, final String serviceName, final String serviceIdentifier) {
+    private void testDefaults(final UUID destinationId, final String serviceIdentifier) {
         if (destinationId == null) {
             throw new IllegalArgumentException("DestinationId is mandatory, but was null.");
         } else if (serviceIdentifier == null) {
@@ -68,14 +67,13 @@ public class ValidDataGuard {
             throw new IllegalArgumentException("LeikaKey has invalid format, please follow: ^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$.");
         }
 
-        Destination destination = sender.getDestination(destinationId);
+        final Destination destination = sender.getDestination(destinationId);
         if (serviceTypeDoesNotMatchDestination(destination, serviceIdentifier)) {
             throw new IllegalArgumentException("Provided service type '" + serviceIdentifier + "' is not allowed by the destination ");
         }
     }
 
     private void testDefaults(final SendableSubmission sendableSubmission) {
-
         if (sendableSubmission.getDestinationId() == null) {
             throw new IllegalArgumentException("DestinationId is mandatory, but was null.");
         } else if (sendableSubmission.getServiceIdentifier() == null) {
@@ -84,7 +82,7 @@ public class ValidDataGuard {
             throw new IllegalArgumentException("LeikaKey has invalid format, please follow: ^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$.");
         }
 
-        Destination destination = sender.getDestination(sendableSubmission.getDestinationId());
+        final Destination destination = sender.getDestination(sendableSubmission.getDestinationId());
         if (serviceTypeDoesNotMatchDestination(destination, sendableSubmission.getServiceIdentifier())) {
             throw new IllegalArgumentException("Provided service type '" + sendableSubmission.getServiceIdentifier() + "' is not allowed by the destination ");
         }
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/RoutingClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/RouterClientTest.java
similarity index 90%
rename from client/src/test/java/dev/fitko/fitconnect/client/RoutingClientTest.java
rename to client/src/test/java/dev/fitko/fitconnect/client/RouterClientTest.java
index a0ee2b95c8f06a868455ae56c7707f4917b4697a..bddbfdfb8281a39bb794b3645983b50af7fe1b30 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/RoutingClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/RouterClientTest.java
@@ -5,7 +5,7 @@ import dev.fitko.fitconnect.api.domain.model.route.AreaResult;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
 import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.RoutingException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectRouterException;
 import dev.fitko.fitconnect.api.services.routing.RoutingService;
 import dev.fitko.fitconnect.client.router.DestinationSearch;
 import dev.fitko.fitconnect.core.routing.RouteVerifier;
@@ -25,19 +25,19 @@ import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-class RoutingClientTest {
+class RouterClientTest {
 
 
     RoutingService routingServiceMock;
 
     RouteVerifier routeVerifierMock;
-    RoutingClient underTest;
+    RouterClient underTest;
 
     @BeforeEach
     void setup() {
         routingServiceMock = mock(RoutingService.class);
         routeVerifierMock = mock(RouteVerifier.class);
-        underTest = new RoutingClient(routingServiceMock, routeVerifierMock);
+        underTest = new RouterClient(routingServiceMock, routeVerifierMock);
     }
 
     @Test
@@ -82,7 +82,7 @@ class RoutingClientTest {
         expectedRouteResult.setRoutes(List.of(expectedRoute));
 
         when(routingServiceMock.getRoutes(any(), any(), any(), any(), anyInt(), anyInt())).thenReturn(expectedRouteResult);
-        when(routeVerifierMock.validateRouteDestinations(any(), any(), any())).thenReturn(ValidationResult.error(new RoutingException("Route validation failed")));
+        when(routeVerifierMock.validateRouteDestinations(any(), any(), any())).thenReturn(ValidationResult.error(new FitConnectRouterException("Route validation failed", new Exception())));
 
         final DestinationSearch search = DestinationSearch.Builder()
                 .withLeikaKey("99400048079000")
@@ -91,7 +91,7 @@ class RoutingClientTest {
                 .build();
 
         // When
-        final RoutingException exception = assertThrows(RoutingException.class, () -> underTest.findDestinations(search));
+        final FitConnectRouterException exception = assertThrows(FitConnectRouterException.class, () -> underTest.findDestinations(search));
 
         // Then
         assertThat(exception.getMessage(), containsString("Route validation failed"));
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
index e3b9095a362e63ad288a3d7702b19389f223e064..b468f8731a5dfb038a4f9c2d39f937ffaf931441 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/SenderClientTest.java
@@ -17,12 +17,10 @@ import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.AttachmentCreationException;
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
-import dev.fitko.fitconnect.api.exceptions.KeyNotRetrievedException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionNotCreatedException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSenderException;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
@@ -213,10 +211,11 @@ public class SenderClientTest {
                 .setJsonData("{}", URI.create("https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json"))
                 .build();
 
-        final var exception = assertThrows(IllegalArgumentException.class, () -> senderClient.send(submission));
+        final var exception = assertThrows(FitConnectSenderException.class, () -> senderClient.send(submission));
 
         // Then
-        assertThat(exception.getMessage(), equalTo("Combination of provided MIME type 'application/json' and schema URI " +
+        assertThat(exception.getMessage(), equalTo("Sending submission failed."));
+        assertThat(exception.getCause().getMessage(), equalTo("Combination of provided MIME type 'application/json' and schema URI " +
                 "'https://schema.fitko.de/fim/s00000000009_1.0.0.schema.json' is not allowed by the destination"));
     }
 
@@ -265,7 +264,7 @@ public class SenderClientTest {
         final var path = Path.of("/non/existing/path");
 
         // When
-        final AttachmentCreationException exception = assertThrows(AttachmentCreationException.class, () -> Attachment.fromPath(path, "plain/text"));
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> Attachment.fromPath(path, "plain/text"));
 
         // Then
         assertThat(exception.getMessage(), containsString("Reading attachment from path '" + path + "' failed"));
@@ -283,7 +282,7 @@ public class SenderClientTest {
         };
 
         // When
-        final AttachmentCreationException exception = assertThrows(AttachmentCreationException.class, () -> Attachment.fromInputStream(inputStream, "plain/text", "non-existing-file", "test"));
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> Attachment.fromInputStream(inputStream, "plain/text", "non-existing-file", "test"));
 
         // Then
         assertThat(exception.getMessage(), containsString("Attachment could not be read from input-stream"));
@@ -302,18 +301,18 @@ public class SenderClientTest {
                 .build();
 
         // When
-        final var sentSubmission = senderClient.send(submission);
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> senderClient.send(submission));
 
         // Then
-        assertNull(sentSubmission);
-        logs.assertContains("Encrypting submission failed");
+        assertThat(exception.getMessage(), containsString("Sending submission failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Encryption failed"));
     }
 
     @Test
     void testAnnouncingSubmissionFailed() {
 
         // Given
-        when(senderMock.createSubmission(any())).thenThrow(new SubmissionNotCreatedException("Announcing submission failed"));
+        when(senderMock.createSubmission(any())).thenThrow(new RestApiException("Announcing submission failed"));
 
         final var submission = SendableSubmission.Builder()
                 .setDestination(destinationId)
@@ -322,11 +321,11 @@ public class SenderClientTest {
                 .build();
 
         // When
-        final var sentSubmission = senderClient.send(submission);
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> senderClient.send(submission));
 
         // Then
-        assertNull(sentSubmission);
-        logs.assertContains("Failed to announce new submission");
+        assertThat(exception.getMessage(), containsString("Sending submission failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Announcing submission failed"));
 
     }
 
@@ -343,11 +342,11 @@ public class SenderClientTest {
                 .build();
 
         // When
-        final var sentSubmission = senderClient.send(submission);
-
-        assertNull(sentSubmission);
-        logs.assertContains("Sending submission failed");
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> senderClient.send(submission));
 
+        // Then
+        assertThat(exception.getMessage(), containsString("Sending submission failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Announcing submission failed"));
     }
 
     @Test
@@ -355,7 +354,7 @@ public class SenderClientTest {
 
         // Given
 
-        when(senderMock.getEncryptionKeyForDestination(any())).thenThrow(new KeyNotRetrievedException("Getting encryption key failed"));
+        when(senderMock.getEncryptionKeyForDestination(any())).thenThrow(new RestApiException("Getting encryption key failed"));
 
         // When
         final SendableSubmission submission = SendableSubmission.Builder()
@@ -364,10 +363,13 @@ public class SenderClientTest {
                 .setJsonData("{}", URI.create("https://mimetype.test.de"))
                 .build();
 
-        final var sentSubmission = senderClient.send(submission);
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> senderClient.send(submission));
 
-        assertNull(sentSubmission);
-        logs.assertContains("Getting encryption key for destination " + destinationId + " failed");
+        //Then
+        assertThat(exception.getMessage(), containsString("Sending submission failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Getting encryption key failed"));
+
+        //logs.assertContains("Getting encryption key for destination " + destinationId + " failed");
     }
 
     @Test
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java b/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
index ad116a730dcf6f1544427d56595f02b976bf1eb5..5a0eaf56155f44116b057cc0d0adc4bd2656acf8 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/SubscriberClientTest.java
@@ -4,6 +4,8 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.config.Environment;
+import dev.fitko.fitconnect.api.config.EnvironmentName;
 import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
@@ -28,13 +30,13 @@ import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
-import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.AuthenticationTagsEmptyException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSubscriberException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.SubmitEventNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
@@ -82,7 +84,12 @@ class SubscriberClientTest {
 
     @BeforeEach
     public void setup() throws IOException, ParseException {
-        final ApplicationConfig config = new ApplicationConfig();
+        final EnvironmentName environmentName = new EnvironmentName("TESTING");
+        final ApplicationConfig config = ApplicationConfig.builder()
+                .environments(Map.of(environmentName, new Environment()))
+                .activeEnvironment(environmentName)
+                .build();
+
         privateKey = RSAKey.parse(getResourceAsString("private_decryption_test_key.json"));
         subscriberMock = Mockito.mock(Subscriber.class);
         underTest = new SubscriberClient(subscriberMock, new SubmissionReceiver(subscriberMock, privateKey, config));
@@ -104,10 +111,11 @@ class SubscriberClientTest {
         when(subscriberMock.getSubmission(any())).thenThrow(new RestApiException("Submission not found"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Submission not found"));
+        assertThat(exception.getMessage(), containsString("Submission could not be retrieved"));
+        assertThat(exception.getCause().getMessage(), containsString("Submission not found"));
     }
 
     @Test
@@ -129,10 +137,10 @@ class SubscriberClientTest {
         when(subscriberMock.decryptStringContent(any(), any())).thenThrow(new DecryptionException("Decryption failed"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Decryption failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Decryption failed"));
     }
 
     @Test
@@ -250,10 +258,10 @@ class SubscriberClientTest {
         when(subscriberMock.decryptStringContent(any(), any())).thenReturn("encryptedMetadata".getBytes());
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Unrecognized token 'encryptedMetadata'"));
+        assertThat(exception.getCause().getMessage(), containsString("Unrecognized token 'encryptedMetadata'"));
     }
 
     @Test
@@ -345,10 +353,10 @@ class SubscriberClientTest {
         when(subscriberMock.getSubmission(any())).thenThrow(new RestApiException("Submission not found"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(UUID.randomUUID()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Submission not found"));
+        assertThat(exception.getMessage(), containsString("Submission could not be retrieved"));
     }
 
     @Test
@@ -378,10 +386,10 @@ class SubscriberClientTest {
         when(subscriberMock.validateMetadata(any(), any(), any())).thenReturn(ValidationResult.problem(problem));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Metadata is invalid"));
+        assertThat(exception.getCause().getMessage(), containsString("Metadata is invalid"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
     }
 
@@ -545,10 +553,10 @@ class SubscriberClientTest {
         when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Data is invalid"));
+        assertThat(exception.getCause().getMessage(), containsString("Data is invalid"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
 
     }
@@ -602,10 +610,10 @@ class SubscriberClientTest {
         when(subscriberMock.validateData(any(), any(), any(), any())).thenReturn(ValidationResult.problem(problem));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Data is invalid"));
+        assertThat(exception.getCause().getMessage(), containsString("Data is invalid"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
     }
 
@@ -671,10 +679,10 @@ class SubscriberClientTest {
         when(subscriberMock.validateAttachments(anyList(), any())).thenReturn(ValidationResult.problem(problem));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Attachment validation failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Attachment validation failed"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(problem)));
     }
 
@@ -734,10 +742,10 @@ class SubscriberClientTest {
         when(subscriberMock.fetchAttachment(any(), any())).thenThrow(new RestApiException("Attachment download failed", 404));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Attachment download failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Attachment download failed"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(new MissingAttachment(attachment.getAttachmentId()))));
     }
 
@@ -798,10 +806,10 @@ class SubscriberClientTest {
         when(subscriberMock.fetchAttachment(any(), any())).thenReturn("encrypt$dAtt@chm$nt");
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submissionId));
 
         // Then
-        assertThat(exception.getMessage(), containsString("Decrypting attachment failed"));
+        assertThat(exception.getCause().getMessage(), containsString("Decrypting attachment failed"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(new AttachmentEncryptionIssue(attachment.getAttachmentId()))));
     }
 
@@ -818,10 +826,10 @@ class SubscriberClientTest {
         when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new SubmitEventNotFoundException("no submit event in log"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("The Event-Log is inconsistent because it does not contain exactly one 'submit' event."));
+        assertThat(exception.getCause().getMessage(), containsString("The Event-Log is inconsistent because it does not contain exactly one 'submit' event."));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(expectedProblem)));
     }
 
@@ -838,10 +846,10 @@ class SubscriberClientTest {
         when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new AuthenticationTagsEmptyException("empty auth tags"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("The 'submit-submission' event does not contain authentication tags"));
+        assertThat(exception.getCause().getMessage(), containsString("The 'submit-submission' event does not contain authentication tags"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(expectedProblem)));
     }
 
@@ -855,13 +863,13 @@ class SubscriberClientTest {
         final InvalidEventLog expectedProblem = new InvalidEventLog();
 
         when(subscriberMock.getSubmission(submission.getSubmissionId())).thenReturn(submission);
-        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new EventLogException("Invalid log"));
+        when(subscriberMock.getAuthenticationTagsForEvent(Event.SUBMIT, submission)).thenThrow(new EventLogException("The Event-Log is inconsistent"));
 
         // When
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> underTest.requestSubmission(submission.getSubmissionId()));
 
         // Then
-        assertThat(exception.getMessage(), containsString("The Event-Log is inconsistent"));
+        assertThat(exception.getCause().getMessage(), containsString("The Event-Log is inconsistent"));
         verify(subscriberMock, times(1)).rejectSubmission(EventPayload.forRejectEvent(submission, List.of(expectedProblem)));
     }
 
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoaderTest.java b/client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoaderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb2aa55d803d387ccf086e4387fa8aa125796c0f
--- /dev/null
+++ b/client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ApplicationConfigLoaderTest.java
@@ -0,0 +1,190 @@
+package dev.fitko.fitconnect.client.bootstrap;
+
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.config.Environment;
+import dev.fitko.fitconnect.api.config.EnvironmentName;
+import dev.fitko.fitconnect.api.config.SenderConfig;
+import dev.fitko.fitconnect.api.config.defaults.DefaultEnvironments;
+import dev.fitko.fitconnect.api.config.defaults.ResourcePaths;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static dev.fitko.fitconnect.api.config.defaults.DefaultEnvironments.PROD;
+import static dev.fitko.fitconnect.api.config.defaults.DefaultEnvironments.STAGE;
+import static dev.fitko.fitconnect.api.config.defaults.DefaultEnvironments.TEST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ApplicationConfigLoaderTest {
+
+    @Test
+    void testLoadMissingConfigFile() {
+        Assertions.assertThrows(FitConnectInitialisationException.class, () -> ApplicationConfigLoader.loadConfigFromPath(Path.of("/no/path")));
+    }
+
+    @Test
+    void testEnvironmentNotAvailable() throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config-with-missing-env.yml");
+        final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromYamlString(testConfigYaml);
+
+        //When
+        final FitConnectInitialisationException exception = Assertions.assertThrowsExactly(FitConnectInitialisationException.class, config::getCurrentEnvironment);
+
+        //Then
+        assertThat(exception.getMessage(), containsString("No environment with name 'ENVIRONMENT_THAT_DOES_NOT_EXIST' found"));
+    }
+
+    @Test
+    void testLoadTestConfigFromString() throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config.yml");
+
+        final Environment devEnv = new Environment(
+                "https://auth-testing.fit-connect.fitko.dev",
+                "https://routing-api-testing.fit-connect.fitko.dev",
+                List.of("https://submission-api-testing.fit-connect.fitko.dev"),
+                "https://portal.auth-testing.fit-connect.fitko.dev",
+                true,
+                false,
+                false
+        );
+
+        final SenderConfig senderConfig = new SenderConfig("1", "123");
+
+        // When
+        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromYamlString(testConfigYaml);
+
+        // Then
+        assertNotNull(testConfig);
+        assertThat(testConfig.getCurrentEnvironment(), equalTo(devEnv));
+        assertThat(testConfig.getSenderConfig(), equalTo(senderConfig));
+    }
+
+    @Test
+    void testLoadTestConfigFromPath(@TempDir final Path tempDir) throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config.yml");
+
+        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config.yml"), testConfigYaml);
+
+        // When
+        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromPath(configPath);
+
+        // Then
+        assertNotNull(testConfig);
+        assertThat(testConfig.getActiveEnvironment(), is(new EnvironmentName("TEST")));
+    }
+
+    @Test
+    void testSubscriberConfigContainsListOfPrivateDecryptionKeys(@TempDir final Path tempDir) throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config.yml");
+        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config.yml"), testConfigYaml);
+
+        // When
+        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromPath(configPath);
+
+        // Then
+        assertNotNull(testConfig);
+        assertThat(testConfig.getSubscriberConfig().getPrivateDecryptionKeyPaths(), hasSize(2));
+    }
+
+    @Test
+    void testUseDefaultEnvironment(@TempDir final Path tempDir) throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config-default.yml");
+        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config-default.yml"), testConfigYaml);
+
+        // When
+        final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(configPath);
+
+        // Then
+        assertEquals(DefaultEnvironments.getEnvironmentsAsMap().size(), config.getEnvironments().size());
+
+        assertThat(config.getEnvironments().get(PROD.getEnvironmentName()), is(PROD.getEnvironment()));
+        assertThat(config.getEnvironments().get(STAGE.getEnvironmentName()), is(STAGE.getEnvironment()));
+        assertThat(config.getEnvironments().get(TEST.getEnvironmentName()), is(TEST.getEnvironment()));
+
+    }
+
+    @Test
+    void testOverridePropertiesOnDefaultEnvironment(@TempDir final Path tempDir) throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config.yml");
+        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config.yml"), testConfigYaml);
+
+        assertTrue(TEST.getEnvironment().getAllowInsecurePublicKey());
+
+        // When
+        final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(configPath);
+        final Environment envWithChangedDefaults = config.getEnvironments().get(TEST.getEnvironmentName());
+
+        // Then
+        assertFalse(envWithChangedDefaults.getAllowInsecurePublicKey());
+    }
+
+    @Test
+    void testLoadingAdditionalCustomEnvironment(@TempDir final Path tempDir) throws IOException {
+
+        // Given
+        final String testConfigYaml = getResourceAsString("test-config-with-custom-env.yml");
+        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config-with-custom-env.yml"), testConfigYaml);
+
+        final Environment customEnvironment = new Environment();
+        customEnvironment.setEnableAutoReject(true);
+        customEnvironment.setAllowInsecurePublicKey(false);
+        customEnvironment.setSkipSubmissionDataValidation(false);
+        customEnvironment.setAuthBaseUrl("https://auth-url.test.net");
+        customEnvironment.setRoutingBaseUrl("https://routing-url.test.net");
+        customEnvironment.setSubmissionBaseUrls(List.of("https://submission-url-1.test.net", "https://submission-url-2.test.net"));
+        customEnvironment.setSelfServicePortalBaseUrl("https://portal-url.test.net");
+
+        // When
+        final ApplicationConfig configWithCustomEnvironment = ApplicationConfigLoader.loadConfigFromPath(configPath);
+
+        // Then
+        assertNotNull(configWithCustomEnvironment);
+
+        final Map<EnvironmentName, Environment> environments = configWithCustomEnvironment.getEnvironments();
+
+        assertThat(environments.size(), is(DefaultEnvironments.getEnvironmentsAsMap().size() + 1));
+        assertThat(environments.get(new EnvironmentName("CUSTOM_ENVIRONMENT")), is(customEnvironment));
+
+        // currently there is only one url supported
+        assertThat(configWithCustomEnvironment.getSubmissionEndpoint(), is("https://submission-url-1.test.net" + ResourcePaths.SUBMISSION_PATH));
+
+        assertThat(environments.get(PROD.getEnvironmentName()), is(PROD.getEnvironment()));
+        assertThat(environments.get(STAGE.getEnvironmentName()), is(STAGE.getEnvironment()));
+        assertThat(environments.get(TEST.getEnvironmentName()), is(TEST.getEnvironment()));
+    }
+
+    private String getResourceAsString(final String name) throws IOException {
+        final ClassLoader classLoader = getClass().getClassLoader();
+        final File file = new File(Objects.requireNonNull(classLoader.getResource(name)).getFile());
+        return Files.readString(file.toPath());
+    }
+}
\ No newline at end of file
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/factory/ClientFactoryTest.java b/client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ClientFactoryTest.java
similarity index 79%
rename from client/src/test/java/dev/fitko/fitconnect/client/factory/ClientFactoryTest.java
rename to client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ClientFactoryTest.java
index 3967ac3ef8365520fdddee7ac4379d046f7c808b..252ee3444ad1ae4c0cb02216cbf3a4b51b12f729 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/factory/ClientFactoryTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/bootstrap/ClientFactoryTest.java
@@ -1,8 +1,11 @@
-package dev.fitko.fitconnect.client.factory;
-
-import dev.fitko.fitconnect.api.config.*;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
+package dev.fitko.fitconnect.client.bootstrap;
+
+import dev.fitko.fitconnect.api.config.ApplicationConfig;
+import dev.fitko.fitconnect.api.config.Environment;
+import dev.fitko.fitconnect.api.config.EnvironmentName;
+import dev.fitko.fitconnect.api.config.SenderConfig;
+import dev.fitko.fitconnect.api.config.SubscriberConfig;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
@@ -17,16 +20,16 @@ class ClientFactoryTest {
 
     @Test
     void testMissingConfiguration() {
-        assertThrows(InitializationException.class, () -> ClientFactory.getSenderClient(null));
-        assertThrows(InitializationException.class, () -> ClientFactory.getSubscriberClient(null));
-        assertThrows(InitializationException.class, () -> ClientFactory.getRoutingClient(null));
+        assertThrows(FitConnectInitialisationException.class, () -> ClientFactory.getSenderClient(null));
+        assertThrows(FitConnectInitialisationException.class, () -> ClientFactory.getSubscriberClient(null));
+        assertThrows(FitConnectInitialisationException.class, () -> ClientFactory.getRouterClient(null));
     }
 
     @Test
     void testSenderClientConstruction() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environments = Map.of(envName, new Environment("https://auth", "", "", "", true, false));
+        final var environments = Map.of(envName, new Environment("https://auth", "", List.of(), "", true, false, false));
 
         final var sender = new SenderConfig("123", "abc");
 
@@ -45,7 +48,7 @@ class ClientFactoryTest {
     void testSubscriberClientConstruction() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environments = Map.of(envName, new Environment("https://auth", "", "", "", true, false));
+        final var environments = Map.of(envName, new Environment("https://auth", "", List.of(), "", true, false, false));
 
         final var subscriber = SubscriberConfig.builder()
                 .clientSecret("123")
@@ -66,10 +69,10 @@ class ClientFactoryTest {
     }
 
     @Test
-    void testRoutingClientConstruction() {
+    void testRouterClientConstruction() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environment = new Environment("", "https://routing.fitko.fitconnect.de", "", "", true, false);
+        final var environment = new Environment("", "https://routing.fitko.fitconnect.de", List.of(), "", true, false, false);
 
         final var senderConfig = new SenderConfig("1234", "abcd");
 
@@ -79,14 +82,14 @@ class ClientFactoryTest {
                 .senderConfig(senderConfig)
                 .build();
 
-        assertNotNull(ClientFactory.getRoutingClient(routingConfig));
+        assertNotNull(ClientFactory.getRouterClient(routingConfig));
     }
 
     @Test
     void testSigningKeyCannotBeParsed() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environments = Map.of(envName, new Environment("https://auth", "", "", "", true, false));
+        final var environments = Map.of(envName, new Environment("https://auth", "", List.of(), "", true, false, false));
 
         final var subscriberConfig = SubscriberConfig.builder()
                 .clientSecret("123")
@@ -104,7 +107,7 @@ class ClientFactoryTest {
                 .build();
 
         assertThrows(
-                InvalidKeyException.class,
+                FitConnectInitialisationException.class,
                 () -> ClientFactory.getSubscriberClient(config),
                 "Expected new ClientFactoryTest() to throw, but nothing was thrown"
         );
@@ -114,7 +117,7 @@ class ClientFactoryTest {
     void testDecryptionKeyCannotBeParsed() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environments = Map.of(envName, new Environment("https://auth", "", "", "", true, false));
+        final var environments = Map.of(envName, new Environment("https://auth", "", List.of(), "", true, false, false));
 
         final var subscriberConfigWithoutKey = SubscriberConfig.builder()
                 .clientSecret("123")
@@ -132,7 +135,7 @@ class ClientFactoryTest {
                 .build();
 
         assertThrows(
-                InvalidKeyException.class,
+                FitConnectInitialisationException.class,
                 () -> ClientFactory.getSubscriberClient(config));
     }
 
@@ -140,7 +143,7 @@ class ClientFactoryTest {
     void testMultipleDecryptionKeysNotYetSupported() {
 
         final var envName = new EnvironmentName("DEV");
-        final var environments = Map.of(envName, new Environment("https://auth", "", "", "", true, false));
+        final var environments = Map.of(envName, new Environment("https://auth", "", List.of(), "", true, false, false));
 
         final var subscriberConfigWithoutKey = SubscriberConfig.builder()
                 .clientSecret("123")
@@ -157,8 +160,8 @@ class ClientFactoryTest {
                 .activeEnvironment(envName)
                 .build();
 
-        final InitializationException exception = assertThrows(
-                InitializationException.class,
+        final FitConnectInitialisationException exception = assertThrows(
+                FitConnectInitialisationException.class,
                 () -> ClientFactory.getSubscriberClient(config));
 
         assertThat(exception.getMessage(), containsString("Currently only one configured private key per subscriber is allowed !"));
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoaderTest.java b/client/src/test/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoaderTest.java
index a0b7d85cd63165022f11a567039a1e2b91430432..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/client/src/test/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoaderTest.java
+++ b/client/src/test/java/dev/fitko/fitconnect/client/factory/ApplicationConfigLoaderTest.java
@@ -1,108 +0,0 @@
-package dev.fitko.fitconnect.client.factory;
-
-import dev.fitko.fitconnect.api.config.ApplicationConfig;
-import dev.fitko.fitconnect.api.config.Environment;
-import dev.fitko.fitconnect.api.config.EnvironmentName;
-import dev.fitko.fitconnect.api.config.SenderConfig;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Objects;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class ApplicationConfigLoaderTest {
-
-    @Test
-    void testLoadMissingConfigFile() {
-        Assertions.assertThrows(InitializationException.class, () -> ApplicationConfigLoader.loadConfigFromPath(Path.of("/no/path")));
-    }
-
-    @Test
-    void testEnvironmentNotAvailable() throws IOException {
-
-        // Given
-        final String testConfigYaml = getResourceAsString("test-config-with-missing-env.yml");
-        final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromYamlString(testConfigYaml);
-
-        //When
-        final InitializationException exception = Assertions.assertThrowsExactly(InitializationException.class, config::getCurrentEnvironment);
-
-        //Then
-        assertThat(exception.getMessage(), containsString("Available environments are: prod | dev | testing"));
-    }
-
-    @Test
-    void testLoadTestConfigFromString() throws IOException {
-
-        // Given
-        final String testConfigYaml = getResourceAsString("test-config.yml");
-
-        final Environment devEnv = new Environment(
-                "https://auth-testing.fit-connect.fitko.dev",
-                "https://routing-api-testing.fit-connect.fitko.dev",
-                "https://submission-api-testing.fit-connect.fitko.dev",
-                "https://portal.auth-testing.fit-connect.fitko.dev",
-                true,
-                false
-        );
-
-        final SenderConfig senderConfig = new SenderConfig("1", "123");
-
-        // When
-        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromYamlString(testConfigYaml);
-
-        // Then
-        assertNotNull(testConfig);
-        assertThat(testConfig.getCurrentEnvironment(), equalTo(devEnv));
-        assertThat(testConfig.getSenderConfig(), equalTo(senderConfig));
-    }
-
-    @Test
-    void testLoadTestConfigFromPath(@TempDir final Path tempDir) throws IOException {
-
-        // Given
-        final String testConfigYaml = getResourceAsString("test-config.yml");
-
-        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config.yml"), testConfigYaml);
-
-        // When
-        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromPath(configPath);
-
-        // Then
-        assertNotNull(testConfig);
-        assertThat(testConfig.getActiveEnvironment(), is(new EnvironmentName("dev")));
-    }
-
-    @Test
-    void testSubscriberConfigContainsListOfPrivateDecryptionKeys(@TempDir final Path tempDir) throws IOException {
-
-        // Given
-        final String testConfigYaml = getResourceAsString("test-config.yml");
-        final Path configPath = Files.writeString(Path.of(tempDir.toString(), "test-config.yml"), testConfigYaml);
-
-        // When
-        final ApplicationConfig testConfig = ApplicationConfigLoader.loadConfigFromPath(configPath);
-
-        // Then
-        assertNotNull(testConfig);
-        assertThat(testConfig.getSubscriberConfig().getPrivateDecryptionKeyPaths(), hasSize(2));
-    }
-
-    private String getResourceAsString(final String name) throws IOException {
-        final ClassLoader classLoader = getClass().getClassLoader();
-        final File file = new File(Objects.requireNonNull(classLoader.getResource(name)).getFile());
-        return Files.readString(file.toPath());
-    }
-}
\ No newline at end of file
diff --git a/client/src/test/resources/test-config-default.yml b/client/src/test/resources/test-config-default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de05efbab54374e95e750ee802b44da3f13a4960
--- /dev/null
+++ b/client/src/test/resources/test-config-default.yml
@@ -0,0 +1,12 @@
+senderConfig:
+  clientId: "1"
+  clientSecret: "123"
+  
+subscriberConfig:
+  clientId: "2"
+  clientSecret: "456"
+  privateDecryptionKeyPaths: ["client/src/test/java/resources/private_decryption_test_key.json", "path/to/fake_key.json"]
+  privateSigningKeyPath: "client/src/test/java/resources/private_test_signing_key.json"
+
+activeEnvironment: PROD
+
diff --git a/client/src/test/resources/test-config-with-custom-env.yml b/client/src/test/resources/test-config-with-custom-env.yml
new file mode 100644
index 0000000000000000000000000000000000000000..22ff56d499758d14dd0354b8434b5c08c4bb3a0c
--- /dev/null
+++ b/client/src/test/resources/test-config-with-custom-env.yml
@@ -0,0 +1,21 @@
+senderConfig:
+  clientId: "1"
+  clientSecret: "123"
+  
+subscriberConfig:
+  clientId: "2"
+  clientSecret: "456"
+  privateDecryptionKeyPaths: ["client/src/test/java/resources/private_decryption_test_key.json", "path/to/fake_key.json"]
+  privateSigningKeyPath: "client/src/test/java/resources/private_test_signing_key.json"
+
+activeEnvironment: CUSTOM_ENVIRONMENT
+
+environments:
+  CUSTOM_ENVIRONMENT:
+    authBaseUrl: "https://auth-url.test.net"
+    routingBaseUrl: "https://routing-url.test.net"
+    submissionBaseUrls: ["https://submission-url-1.test.net", "https://submission-url-2.test.net"]
+    selfServicePortalBaseUrl: "https://portal-url.test.net"
+    enableAutoReject: true
+    allowInsecurePublicKey: false
+    skipSubmissionDataValidation: false
diff --git a/client/src/test/resources/test-config-with-missing-env.yml b/client/src/test/resources/test-config-with-missing-env.yml
index 06f2c02522a284e94c724ff1694861291ca7ba93..5e7814b8783170883993255e1158253a4f55928d 100644
--- a/client/src/test/resources/test-config-with-missing-env.yml
+++ b/client/src/test/resources/test-config-with-missing-env.yml
@@ -8,24 +8,5 @@ subscriberConfig:
   privateDecryptionKeyPaths: ["client/src/test/java/resources/private_decryption_test_key.json"]
   privateSigningKeyPath: "client/src/test/java/resources/private_test_signing_key.json"
 
-activeEnvironment: environment_that_does_not_exist
+activeEnvironment: ENVIRONMENT_THAT_DOES_NOT_EXIST
 
-environments:
-  prod:
-    authBaseUrl: "https://auth-prod.fit-connect.fitko.net"
-    routingBaseUrl: "https://routing-api-prod.fit-connect.fitko.net"
-    submissionBaseUrl: "https://submission-api-prod.fit-connect.fitko.net"
-    selfServicePortalBaseUrl: "https://portal.auth-prod.fit-connect.fitko.net"
-    allowInsecurePublicKey: false
-  dev:
-    authBaseUrl: "https://auth-testing.fit-connect.fitko.dev"
-    routingBaseUrl: "https://routing-api-testing.fit-connect.fitko.dev"
-    submissionBaseUrl: "https://submission-api-testing.fit-connect.fitko.dev"
-    selfServicePortalBaseUrl: "https://portal.auth-testing.fit-connect.fitko.dev"
-    allowInsecurePublicKey: true
-  testing:
-    authBaseUrl: "https://auth-testing.fit-connect.fitko.dev"
-    routingBaseUrl: "https://routing-api-testing.fit-connect.fitko.dev"
-    submissionBaseUrl: "https://submission-api-testing.fit-connect.fitko.dev"
-    selfServicePortalBaseUrl: "https://portal.auth-testing.fit-connect.fitko.dev"
-    allowInsecurePublicKey: true
diff --git a/client/src/test/resources/test-config.yml b/client/src/test/resources/test-config.yml
index bc70ce10c52fb51944035eaa38431eeb3452ca57..f4417751d33707da24cb87ee3658ac88e95317ea 100644
--- a/client/src/test/resources/test-config.yml
+++ b/client/src/test/resources/test-config.yml
@@ -8,24 +8,16 @@ subscriberConfig:
   privateDecryptionKeyPaths: ["client/src/test/java/resources/private_decryption_test_key.json", "path/to/fake_key.json"]
   privateSigningKeyPath: "client/src/test/java/resources/private_test_signing_key.json"
 
-activeEnvironment: dev
+activeEnvironment: TEST
 
 environments:
-  prod:
-    authBaseUrl: "https://auth-prod.fit-connect.fitko.net"
-    routingBaseUrl: "https://routing-api-prod.fit-connect.fitko.net"
-    submissionBaseUrl: "https://submission-api-prod.fit-connect.fitko.net"
-    selfServicePortalBaseUrl: "https://portal.auth-prod.fit-connect.fitko.net"
+  PROD:
+    enableAutoReject: true
     allowInsecurePublicKey: false
-  dev:
-    authBaseUrl: "https://auth-testing.fit-connect.fitko.dev"
-    routingBaseUrl: "https://routing-api-testing.fit-connect.fitko.dev"
-    submissionBaseUrl: "https://submission-api-testing.fit-connect.fitko.dev"
-    selfServicePortalBaseUrl: "https://portal.auth-testing.fit-connect.fitko.dev"
-    allowInsecurePublicKey: true
-  testing:
-    authBaseUrl: "https://auth-testing.fit-connect.fitko.dev"
-    routingBaseUrl: "https://routing-api-testing.fit-connect.fitko.dev"
-    submissionBaseUrl: "https://submission-api-testing.fit-connect.fitko.dev"
-    selfServicePortalBaseUrl: "https://portal.auth-testing.fit-connect.fitko.dev"
-    allowInsecurePublicKey: true
\ No newline at end of file
+  STAGE:
+    enableAutoReject: true
+    allowInsecurePublicKey: false
+  TEST:
+    enableAutoReject: true
+    allowInsecurePublicKey: false
+    skipSubmissionDataValidation: false
diff --git a/config.yml b/config.yml
index d2234eb6196472ab65a9ceb37f0a55b502e11926..f237f70ac74420c0165eceb3b678ee04034c0f65 100644
--- a/config.yml
+++ b/config.yml
@@ -29,32 +29,35 @@ subscriberConfig:
   privateSigningKeyPath: "path/to/signing_key.json"
 
 # Name of the used environment (from environments config below).
-# In this case the options are prod | testing
-activeEnvironment: testing
+# In this case the options are PROD | STAGE | TEST
+activeEnvironment: TEST
 
-# Configured environments that can be added in the form:
+# environments config
+environments:
+  PROD:
+    enableAutoReject: true
+    allowInsecurePublicKey: false
+    skipSubmissionDataValidation: false
+  TEST:
+    enableAutoReject: true
+    allowInsecurePublicKey: true
+    skipSubmissionDataValidation: false
+
+# Custom environments can be added in the form:
 #
 # name: -- identifier that can be referenced in 'activeEnvironment'
+
 #    authBaseUrl: "URL" -- base URL for OAuth requests
 #    routingBaseUrl: "URL" -- base URL for routing and area search requests
 #    submissionBaseUrl: "URL" -- base URL for submission/destination requests
-#    allowInsecurePublicKey: true | false -- allow public keys that failed a validation for e.g. testing purposes
+#    selfServicePortalBaseUrl: "URL" -- base URL for portal/public key requests
+
+#    enableAutoReject: true | false -- automatically reject submission on receive if a validation failed
+#    allowInsecurePublicKey: true | false -- allow insecure public keys that failed a validation for e.g. testing purposes
+#    skipSubmissionDataValidation: true | false -- allow data validation against a given schema to be switched on/off
 #
 # also see: https://docs.fitko.de/fit-connect/docs/apis/submission-api
 
-environments:
-  prod:
-    authBaseUrl: "https://auth-prod.fit-connect.fitko.net"
-    routingBaseUrl: "https://routing-api-prod.fit-connect.fitko.net"
-    submissionBaseUrl: "https://submission-api-prod.fit-connect.fitko.net"
-    selfServicePortalBaseUrl: "https://portal.auth-prod.fit-connect.fitko.net"
-    allowInsecurePublicKey: false
-  testing:
-    authBaseUrl: "https://auth-testing.fit-connect.fitko.dev"
-    routingBaseUrl: "https://routing-api-testing.fit-connect.fitko.dev"
-    submissionBaseUrl: "https://submission-api-testing.fit-connect.fitko.dev"
-    selfServicePortalBaseUrl: "https://portal.auth-testing.fit-connect.fitko.dev"
-    allowInsecurePublicKey: true
 
 #####################################################################
 # OPTIONAL SETTINGS                                                 #
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
index b21ceb2876e08a01226566d6c522f801dd5dff13..611403975a434798d7bc409cce130ffae6566151 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSender.java
@@ -11,6 +11,10 @@ import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.InvalidKeyException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
@@ -61,12 +65,12 @@ public class SubmissionSender implements Sender {
     }
 
     @Override
-    public String encryptBytes(final RSAKey publicKey, final byte[] data) {
+    public String encryptBytes(final RSAKey publicKey, final byte[] data) throws EncryptionException {
         return cryptoService.encryptBytes(publicKey, data);
     }
 
     @Override
-    public String encryptObject(final RSAKey encryptionKey, final Object obj) {
+    public String encryptObject(final RSAKey encryptionKey, final Object obj) throws EncryptionException {
         return cryptoService.encryptObject(encryptionKey, obj);
     }
 
@@ -77,31 +81,31 @@ public class SubmissionSender implements Sender {
     }
 
     @Override
-    public SubmissionForPickup createSubmission(final CreateSubmission submission) {
+    public SubmissionForPickup createSubmission(final CreateSubmission submission) throws RestApiException {
         LOGGER.info("Announcing new submission for destination {}", submission.getDestinationId());
         return submissionService.announceSubmission(submission);
     }
 
     @Override
-    public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) {
+    public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) throws RestApiException{
         LOGGER.info("Uploading attachment {} for submission {}", attachmentId, submissionId);
         submissionService.uploadAttachment(submissionId, attachmentId, encryptedAttachment);
     }
 
     @Override
-    public Destination getDestination(final UUID destinationId) {
+    public Destination getDestination(final UUID destinationId)  throws RestApiException {
         LOGGER.info("Loading  destination {}", destinationId);
         return submissionService.getDestination(destinationId);
     }
 
     @Override
-    public RSAKey getEncryptionKeyForDestination(final UUID destinationId) {
+    public RSAKey getEncryptionKeyForDestination(final UUID destinationId) throws RestApiException, InvalidKeyException {
         LOGGER.info("Loading encryption key for destination id {}", destinationId);
         return keyService.getPublicEncryptionKey(destinationId);
     }
 
     @Override
-    public Submission sendSubmission(final SubmitSubmission submission) {
+    public Submission sendSubmission(final SubmitSubmission submission) throws RestApiException {
         LOGGER.info("Sending submission {}", submission.getSubmissionId());
         return submissionService.sendSubmission(submission);
     }
@@ -119,13 +123,13 @@ public class SubmissionSender implements Sender {
     }
 
     @Override
-    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) {
+    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException, EventLogException {
         LOGGER.info("Loading event log for destination {}", destinationId);
         return eventLogService.getEventLog(caseId, destinationId);
     }
 
     @Override
-    public SubmissionStatus getLastedEvent(final SentSubmission sentSubmission) {
+    public SubmissionStatus getLastedEvent(final SentSubmission sentSubmission) throws RestApiException, EventLogException {
         LOGGER.info("Loading latest status for submission {}", sentSubmission.getSubmissionId());
         return eventLogService.getLastedEvent(sentSubmission.getDestinationId(), sentSubmission.getCaseId(), sentSubmission.getSubmissionId(), sentSubmission.getAuthenticationTags());
     }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
index a5caafd681e38161f8999f5e753e574efc24da91..376de1ac15e81a389545a4a71dc1addca287f5a9 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/SubmissionSubscriber.java
@@ -13,9 +13,10 @@ import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForVa
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
@@ -59,13 +60,13 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public Set<SubmissionForPickup> pollAvailableSubmissionsForDestination(final UUID destinationId, final int offset, final int limit) {
+    public Set<SubmissionForPickup> pollAvailableSubmissionsForDestination(final UUID destinationId, final int offset, final int limit) throws RestApiException {
         LOGGER.info("Loading submission {}-{} for destination {}", offset, limit, destinationId);
         return submissionService.pollAvailableSubmissionsForDestination(destinationId, offset, limit).getSubmissions();
     }
 
     @Override
-    public Set<SubmissionForPickup> pollAvailableSubmissions(final int offset, final int limit) {
+    public Set<SubmissionForPickup> pollAvailableSubmissions(final int offset, final int limit) throws RestApiException {
         LOGGER.info("Loading submission {}-{}", offset, limit);
         return submissionService.pollAvailableSubmissions(offset, limit).getSubmissions();
     }
@@ -83,24 +84,24 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException {
+    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException, EventLogException {
         LOGGER.info("Loading event log for destination {}", destinationId);
         return eventLogService.getEventLog(caseId, destinationId);
     }
     @Override
-    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) {
+    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) throws RestApiException, EventLogException {
         LOGGER.info("Loading authentication tags of {} event for submission {}", event, submission.getSubmissionId());
         return eventLogService.getAuthenticationTagsForEvent(event, submission);
     }
 
     @Override
-    public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId) throws RestApiException {
+    public SubmissionStatus getLastedEvent(final UUID destinationId, final UUID caseId, final UUID submissionId) throws RestApiException, EventLogException {
         LOGGER.info("Loading current status for submission {}", submissionId);
         return eventLogService.getLastedEventWithoutAuthTagValidation(destinationId, caseId, submissionId);
     }
 
     @Override
-    public ValidationResult validateMetadata(final Metadata metadata, final Submission submission, final AuthenticationTags authenticationTags) {
+    public ValidationResult validateMetadata(final Metadata metadata, final Submission submission, final AuthenticationTags authenticationTags) throws RestApiException {
         LOGGER.info("Validating metadata");
         final Destination destination = submissionService.getDestination(submission.getDestinationId());
         return validationService.validateMetadata(metadata, submission, destination, authenticationTags);
@@ -119,9 +120,9 @@ public class SubmissionSubscriber implements Subscriber {
     }
 
     @Override
-    public ValidationResult validateSubmissionDataSchema(final String submissiondata, final URI schemaUri) {
+    public ValidationResult validateSubmissionDataSchema(final String submissionData, final URI schemaUri) {
         LOGGER.info("Validating submission data schema");
-        return validationService.validateSubmissionDataSchema(submissiondata, schemaUri);
+        return validationService.validateSubmissionDataSchema(submissionData, schemaUri);
     }
 
     @Override
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/auth/DefaultOAuthService.java b/core/src/main/java/dev/fitko/fitconnect/core/auth/DefaultOAuthService.java
index 53989cd4fed0369df177b24d8a15711c8aeea2a4..5e66adbc33c76f1c72fe9930214f1984eabeb8c1 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/auth/DefaultOAuthService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/auth/DefaultOAuthService.java
@@ -1,8 +1,7 @@
 package dev.fitko.fitconnect.core.auth;
 
 import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
-import dev.fitko.fitconnect.api.exceptions.AuthenticationException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.core.http.HttpClient;
 import org.slf4j.Logger;
@@ -63,7 +62,7 @@ public class DefaultOAuthService implements OAuthService {
         tokenExpirationTime = null;
     }
 
-    private void authenticate() throws AuthenticationException {
+    private void authenticate() {
         final String requestBody = buildRequestBody(clientId, clientSecret);
         currentToken = performTokenRequest(requestBody);
         tokenExpirationTime = LocalDateTime.now().plusSeconds(currentToken.getExpiresIn());
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/CryptoConstants.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/CryptoConstants.java
index c723046e8b9a3e34fb6ca888b4c24cdfddf409ab..863677df18f088d58c57731237c665ce22f1f0e3 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/CryptoConstants.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/CryptoConstants.java
@@ -7,8 +7,8 @@ import com.nimbusds.jose.JWEAlgorithm;
 public class CryptoConstants {
 
     public final static String DEFAULT_HASH_ALGORITHM = HashAlgorithm.SHA_512.getIdentifyer();
-    public final static String DEFAULT_SYMMETRIC_ENCRYPTION_ALGORITHM = SymmetricEncryptionAlgorithm.AES.getIdentifyer();
-    public final static String DEFAULT_HMAC_ALGORITHM = HmacAlgorithm.HMAC_SHA_512.getIdentifyer();
+    public final static String DEFAULT_SYMMETRIC_ENCRYPTION_ALGORITHM = SymmetricEncryptionAlgorithm.AES.getIdentifier();
+    public final static String DEFAULT_HMAC_ALGORITHM = HmacAlgorithm.HMAC_SHA_512.getIdentifier();
     public final static EncryptionMethod DEFAULT_JWE_ENCRYPTION_METHOD = EncryptionMethod.A256GCM;
     public static final JWEAlgorithm DEFAULT_JWE_ALGORITHM = JWEAlgorithm.RSA_OAEP_256;
     public static final CompressionAlgorithm DEFAULT_JWE_COMPRESSION_ALGORITHM = CompressionAlgorithm.DEF;
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/HashService.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/HashService.java
index 12b95583b05f029056889a6a15e3cdc515f3620a..82b0e4d8a9899ecd30df6f5a6b2ef679f48c0890 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/HashService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/HashService.java
@@ -1,7 +1,7 @@
 package dev.fitko.fitconnect.core.crypto;
 
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
 import dev.fitko.fitconnect.api.services.crypto.MessageDigestService;
 
 import javax.crypto.Mac;
@@ -22,7 +22,7 @@ public class HashService implements MessageDigestService {
         try {
             messageDigest = MessageDigest.getInstance(DEFAULT_HASH_ALGORITHM);
         } catch (final NoSuchAlgorithmException e) {
-            throw new InitializationException(e.getMessage(), e);
+            throw new FitConnectInitialisationException(e.getMessage(), e);
         }
     }
 
@@ -67,7 +67,6 @@ public class HashService implements MessageDigestService {
 
     @Override
     public String calculateHMAC(final String data, final String key) {
-
         try {
             final String hmacAlgorithm = DEFAULT_HMAC_ALGORITHM;
             final SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), hmacAlgorithm);
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/HmacAlgorithm.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/HmacAlgorithm.java
index f468b836928f745c3cd3b5cb36a41923b7387030..6ec7cb8b8cf199895bcf16d5cb1d4ead911a8c89 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/HmacAlgorithm.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/HmacAlgorithm.java
@@ -4,13 +4,13 @@ public enum HmacAlgorithm {
 
     HMAC_SHA_512("HmacSHA512");
 
-    private final String identifyer;
+    private final String identifier;
 
-    HmacAlgorithm(String identifyer) {
-        this.identifyer = identifyer;
+    HmacAlgorithm(final String identifier) {
+        this.identifier = identifier;
     }
 
-    public String getIdentifyer() {
-        return identifyer;
+    public String getIdentifier() {
+        return identifier;
     }
 }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
index cd7232a96ba4a15f251402f217089b6b5d808bcf..1f7b084e78602fcf25e141238fb792d08cdd904e 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/JWECryptoService.java
@@ -9,8 +9,8 @@ import com.nimbusds.jose.Payload;
 import com.nimbusds.jose.crypto.RSADecrypter;
 import com.nimbusds.jose.crypto.RSAEncrypter;
 import com.nimbusds.jose.jwk.RSAKey;
-import dev.fitko.fitconnect.api.exceptions.DecryptionException;
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.DecryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.crypto.MessageDigestService;
 import dev.fitko.fitconnect.core.util.StopWatch;
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/crypto/SymmetricEncryptionAlgorithm.java b/core/src/main/java/dev/fitko/fitconnect/core/crypto/SymmetricEncryptionAlgorithm.java
index 0b7f909a1dd22963ab1afbd3d5bbb33bf8db1c5e..a75a9ad3f6118fd1c59f34cacc79113c7829d359 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/crypto/SymmetricEncryptionAlgorithm.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/crypto/SymmetricEncryptionAlgorithm.java
@@ -4,13 +4,13 @@ public enum SymmetricEncryptionAlgorithm {
 
     AES("AES");
 
-    private final String identifyer;
+    private final String identifier;
 
-    SymmetricEncryptionAlgorithm(String identifyer) {
-        this.identifyer = identifyer;
+    SymmetricEncryptionAlgorithm(final String identifier) {
+        this.identifier = identifier;
     }
 
-    public String getIdentifyer() {
-        return identifyer;
+    public String getIdentifier() {
+        return identifier;
     }
 }
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
index 3f6abdcc0c0ed46747845bf1a9b04313c55a7cab..b960365ca4c3920e09cf43721536ce697a13cd52 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogApiService.java
@@ -10,10 +10,10 @@ import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.AuthenticationTagsEmptyException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.AuthenticationTagsEmptyException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.SubmitEventNotFoundException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
 import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
@@ -59,7 +59,7 @@ public class EventLogApiService implements EventLogService {
     }
 
     @Override
-    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws EventLogException {
+    public List<EventLogEntry> getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException, EventLogException {
 
         final EventLog eventLog = loadEventLog(caseId);
         final List<SignedJWT> jwtEvents = getJWTSFromEvents(eventLog.getEventLogs());
@@ -69,7 +69,7 @@ public class EventLogApiService implements EventLogService {
     }
 
     @Override
-    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) throws EventLogException {
+    public AuthenticationTags getAuthenticationTagsForEvent(final Event event, final Submission submission) throws RestApiException, EventLogException {
 
         final EventLog eventLog = loadEventLog(submission.getCaseId());
         final List<SignedJWT> submitEvents = getJwtsFromEvent(event, submission, eventLog);
@@ -122,7 +122,7 @@ public class EventLogApiService implements EventLogService {
         try {
             return this.httpClient.get(url, getHttpHeaders(MimeTypes.APPLICATION_JSON), EventLog.class).getBody();
         } catch (final RestApiException e) {
-            throw new EventLogException("EventLog query failed", e);
+            throw new RestApiException("EventLog query failed", e);
         }
     }
 
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
index e77cf01de403b159cbafdadb1b73259832204bf3..dda3ba6a882301774ac16af3baef11fd41ba0c30 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/EventLogVerifier.java
@@ -14,7 +14,7 @@ import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
 import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java b/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
index 0191811982550a7774f4cea128b76a09de31eeec..ac366d87c77505bc1ae3958b1ec2ce984f2fcff8 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/events/SecurityEventTokenService.java
@@ -15,8 +15,8 @@ import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
 import dev.fitko.fitconnect.api.domain.model.event.EventClaimFields;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.events.SecurityEventService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
 
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/http/UserAgentInterceptor.java b/core/src/main/java/dev/fitko/fitconnect/core/http/UserAgentInterceptor.java
index 6aa9ec61c3240ccf7df75d5336340c792d009468..927269f17b3f3665becc27f92e3b036041a87737 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/http/UserAgentInterceptor.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/http/UserAgentInterceptor.java
@@ -16,10 +16,10 @@ public class UserAgentInterceptor implements Interceptor {
     private final String productVersion;
     private final String commit;
 
-    public UserAgentInterceptor(BuildInfo buildInfo) {
-        this.productName = buildInfo.getProductName();
-        this.productVersion = buildInfo.getProductVersion();
-        this.commit = buildInfo.getCommit();
+    public UserAgentInterceptor(final BuildInfo buildInfo) {
+        productName = buildInfo.getProductName();
+        productVersion = buildInfo.getProductVersion();
+        commit = buildInfo.getCommit();
     }
 
     @NotNull
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java b/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
index 4e3c2c2137347681551c575d8eb8cefde377be95..35ccdb32f91518fa19d03e86c81814f8b72c4e84 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/keys/PublicKeyService.java
@@ -9,8 +9,8 @@ import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.domain.model.jwk.ApiJwk;
 import dev.fitko.fitconnect.api.domain.model.jwk.ApiJwkSet;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.InvalidKeyException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.submission.SubmissionService;
@@ -119,7 +119,7 @@ public class PublicKeyService implements KeyService {
 
     private void validateResult(final ValidationResult validationResult, final String message) {
         if (validationResult.hasError()) {
-            if (config.getCurrentEnvironment().isAllowInsecurePublicKey()) {
+            if (config.isAllowInsecurePublicKey()) {
                 LOGGER.warn(message, validationResult.getError());
             } else {
                 throw new InvalidKeyException(message, validationResult.getError());
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/routing/RouteVerifier.java b/core/src/main/java/dev/fitko/fitconnect/core/routing/RouteVerifier.java
index b1920628b492018dd022329d4126b9edc7c6f94c..51a03baefe70e57e22a4091e3785ad07bc549abe 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/routing/RouteVerifier.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/routing/RouteVerifier.java
@@ -16,9 +16,9 @@ import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
 import dev.fitko.fitconnect.api.domain.model.route.RouteDestination;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.InvalidKeyException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.routing.RoutingVerificationService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
@@ -64,7 +64,7 @@ public class RouteVerifier implements RoutingVerificationService {
         } catch (final ValidationException e) {
             return error(e);
         } catch (final InvalidKeyException e) {
-            return error(new ValidationException("Public signature key is invalid: " + e.getMessage()));
+            return error(new ValidationException("Public signature key is invalid: " + e.getCause().getMessage()));
         } catch (final RestApiException e) {
             return error(new ValidationException("Could not retrieve public signature key: " + e.getMessage()));
         } catch (final ParseException | JOSEException | JsonProcessingException e) {
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/routing/RoutingApiService.java b/core/src/main/java/dev/fitko/fitconnect/core/routing/RoutingApiService.java
index b0517a27f4f35b196510f156012355e971d435e1..59b59c891285e531d044c5db164ff6536a21088e 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/routing/RoutingApiService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/routing/RoutingApiService.java
@@ -3,7 +3,7 @@ package dev.fitko.fitconnect.core.routing;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.domain.model.route.AreaResult;
 import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.routing.RoutingService;
 import dev.fitko.fitconnect.core.http.HttpClient;
 import dev.fitko.fitconnect.core.http.HttpHeaders;
@@ -30,7 +30,7 @@ public class RoutingApiService implements RoutingService {
     }
 
     @Override
-    public AreaResult getAreas(final List<String> searchExpressions, final int offset, final int limit) {
+    public AreaResult getAreas(final List<String> searchExpressions, final int offset, final int limit) throws RestApiException {
 
         final String url = config.getAreaEndpoint();
 
@@ -48,7 +48,7 @@ public class RoutingApiService implements RoutingService {
     }
 
     @Override
-    public RouteResult getRoutes(final String leikaKey, final String ars, final String ags, final String areaId, final int offset, final int limit) {
+    public RouteResult getRoutes(final String leikaKey, final String ars, final String ags, final String areaId, final int offset, final int limit) throws RestApiException {
 
         testIfSearchCriterionIsSet(ars, ags, areaId);
 
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java b/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
index da51addd8dbecb5a03278360f3cb4078be6aa2fb..1465c37a681ea8945333e5f823209525c0d553c4 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/schema/SchemaResourceProvider.java
@@ -2,10 +2,10 @@ package dev.fitko.fitconnect.core.schema;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
-import dev.fitko.fitconnect.api.exceptions.InitializationException;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
+import dev.fitko.fitconnect.api.exceptions.internal.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import dev.fitko.fitconnect.core.http.HttpClient;
 import org.slf4j.Logger;
@@ -124,14 +124,15 @@ public class SchemaResourceProvider implements SchemaProvider {
         }
         LOGGER.warn("Fetching schema " + schemaUri + " from local files failed, attempting to retrieve a remote version.");
 
-        if (schemaUri.getScheme().equals("https")) {
-            try {
-                return this.httpClient.get(schemaUri.toString(), String.class).getBody();
-            } catch (Exception exception) {
-                throw new SchemaNotFoundException("Submission data schema " + schemaUri + " is not available.");
-            }
+        if (!schemaUri.getScheme().equals("https")) {
+            throw new SchemaNotFoundException("Fetching schema " + schemaUri + " from remote was skipped, since the URI does not support HTTPS.");
+        }
+
+        try {
+            return this.httpClient.get(schemaUri.toString(), String.class).getBody();
+        } catch (final Exception exception) {
+            throw new SchemaNotFoundException("Submission data schema " + schemaUri + " is not available.");
         }
-        throw new SchemaNotFoundException("Fetching schema " + schemaUri + " from remote was skipped, since the URI does not support HTTPS.");
     }
 
     private void addSetSchema(final String schema) {
@@ -176,7 +177,7 @@ public class SchemaResourceProvider implements SchemaProvider {
 
     private List<String> getResourceFiles(final String schemaPath) {
 
-        File schemaFolder = new File(Objects.requireNonNull(SchemaResourceProvider.class.getClassLoader().getResource(schemaPath)).getFile());
+        final File schemaFolder = new File(Objects.requireNonNull(SchemaResourceProvider.class.getClassLoader().getResource(schemaPath)).getFile());
 
         return Arrays.stream(Objects.requireNonNull(schemaFolder.listFiles()))
                 .map(file -> "/" + schemaPath + "/" + file.getName())
@@ -188,7 +189,7 @@ public class SchemaResourceProvider implements SchemaProvider {
              final BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
             return reader.lines().collect(Collectors.joining(System.lineSeparator()));
         } catch (final Exception e) {
-            throw new InitializationException("Could not read schema file " + schemaFile, e);
+            throw new FitConnectInitialisationException("Could not read schema file " + schemaFile, e);
         }
     }
 
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java b/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
index 2cb475d6ad31c7dd83f63c3726efbc50530b4090..2d70b8131b9765298513d64d6071f8c4146c2c75 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/submission/SubmissionApiService.java
@@ -7,7 +7,7 @@ import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionsForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.submission.SubmissionService;
 import dev.fitko.fitconnect.core.http.HttpClient;
@@ -56,14 +56,17 @@ public class SubmissionApiService implements SubmissionService {
 
     @Override
     public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) throws RestApiException {
-
-        final String url = String.format(config.getAttachmentEndpoint(), submissionId, attachmentId);
-
-        try {
-            this.httpClient.put(url, getHeaders("application/jose"), encryptedAttachment, Void.class);
-        } catch (RestApiException e) {
-            throw new RestApiException("Could not upload attachment", e);
-        }
+        final Map<String, Object> params = new HashMap<>();
+        params.put("submissionId", submissionId);
+        params.put("attachmentId", attachmentId);
+        final RequestSettings requestSettings = RequestSettings.builder()
+                .url(config.getAttachmentEndpoint())
+                .method(HttpMethod.PUT)
+                .responseType(Void.class)
+                .entity(getHttpEntity(encryptedAttachment, getHeaders("application/jose")))
+                .params(params)
+                .build();
+        performRequest(requestSettings);
     }
 
     @Override
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/util/CertificateLoader.java b/core/src/main/java/dev/fitko/fitconnect/core/util/CertificateLoader.java
index ba87f40e8620fce0975fad3f71dcdb3fdb3ff392..0cd61bf249a41aac67513dede44828a4c7ae70c9 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/util/CertificateLoader.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/util/CertificateLoader.java
@@ -1,7 +1,7 @@
 package dev.fitko.fitconnect.core.util;
 
 import com.nimbusds.jose.util.Base64;
-import dev.fitko.fitconnect.api.exceptions.RootCertificateException;
+import dev.fitko.fitconnect.api.exceptions.internal.RootCertificateException;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java b/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
index 00af98f2298134f0e2e577f0387616c4e3e48725..f64826daaeb84726d2c9dc2de7ae888d9a1747d8 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/util/EventLogUtil.java
@@ -14,8 +14,8 @@ import dev.fitko.fitconnect.api.domain.model.event.TypeAndUUID;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
 
 import java.text.ParseException;
 import java.util.ArrayList;
diff --git a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
index 30f40284366243c0fca07bfc3872aa8c64ad467a..e6ec919c3dde7f7dc7dea7191b1a1d995122b20b 100644
--- a/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
+++ b/core/src/main/java/dev/fitko/fitconnect/core/validation/DefaultValidationService.java
@@ -10,7 +10,6 @@ import com.networknt.schema.SpecVersion;
 import com.networknt.schema.ValidationMessage;
 import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
-import com.nimbusds.jose.util.Base64;
 import com.nimbusds.jose.util.StandardCharset;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
@@ -22,6 +21,7 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.Attachmen
 import dev.fitko.fitconnect.api.domain.model.event.problems.attachment.IncorrectAttachmentAuthenticationTag;
 import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataHashMismatch;
 import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataJsonSyntaxViolation;
+import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataSchemaViolation;
 import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataXmlSyntaxViolation;
 import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAuthenticationTag;
 import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.AttachmentsMismatch;
@@ -33,8 +33,6 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.Unsupported
 import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.UnsupportedMetadataSchema;
 import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.UnsupportedReplyChannel;
 import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.UnsupportedService;
-import dev.fitko.fitconnect.api.domain.model.event.problems.data.*;
-import dev.fitko.fitconnect.api.domain.model.event.problems.metadata.*;
 import dev.fitko.fitconnect.api.domain.model.metadata.Metadata;
 import dev.fitko.fitconnect.api.domain.model.metadata.attachment.ApiAttachment;
 import dev.fitko.fitconnect.api.domain.model.metadata.attachment.AttachmentForValidation;
@@ -44,9 +42,9 @@ import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
 import dev.fitko.fitconnect.api.domain.model.replychannel.ReplyChannel;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.DataIntegrityException;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.DataIntegrityException;
+import dev.fitko.fitconnect.api.exceptions.internal.SchemaNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.crypto.MessageDigestService;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
@@ -63,6 +61,7 @@ import org.xml.sax.XMLReader;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParserFactory;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -74,7 +73,13 @@ import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -141,7 +146,7 @@ public class DefaultValidationService implements ValidationService {
             final String metadataJson = MAPPER.writeValueAsString(metadata);
             final JsonNode inputNode = MAPPER.readTree(metadataJson);
             return validate2020JsonSchema(schemaProvider.loadMetadataSchema(config.getMetadataSchemaWriteVersion()), inputNode);
-        } catch (final JsonProcessingException e) {
+        } catch (final JsonProcessingException | SchemaNotFoundException e) {
             // https://docs.fitko.de/fit-connect/docs/receiving/verification/#schema-pr%C3%BCfung
             return ValidationResult.withErrorAndProblem(e, new MetadataSchemaViolation());
         }
@@ -177,12 +182,12 @@ public class DefaultValidationService implements ValidationService {
     @Override
     public ValidationResult validateSubmissionDataSchema(final String json, final URI schemaUri) {
 
-        if (config.getCurrentEnvironment().isSkipSubmissionDataValidation()) {
+        if (config.isSkipSubmissionDataValidation()) {
             LOGGER.warn("Submission data validation is deactivated. This should be done only on secure test environments.");
             return ValidationResult.ok();
         }
 
-        String schema = schemaProvider.loadSubmissionDataSchema(schemaUri);
+        final String schema = schemaProvider.loadSubmissionDataSchema(schemaUri);
         try {
             return returnValidationResult(SCHEMA_FACTORY_DRAFT_2020.getSchema(schema).validate(MAPPER.readTree(json)));
         } catch (final JacksonException e) {
@@ -376,7 +381,7 @@ public class DefaultValidationService implements ValidationService {
     }
 
     private ValidationResult validateKey(final RSAKey publicKey, final KeyOperation purpose) throws JWKValidationException {
-        if (config.getCurrentEnvironment().isAllowInsecurePublicKey()) {
+        if (config.isAllowInsecurePublicKey()) {
             return validateWithoutCertChain(publicKey, purpose);
         } else {
             return validateCertChain(publicKey, purpose);
@@ -397,7 +402,7 @@ public class DefaultValidationService implements ValidationService {
     private ValidationResult validateWithoutCertChain(final RSAKey publicKey, final KeyOperation purpose) {
         LOGGER.info("Validating public key without XC5 certificate chain");
         try {
-            final LogLevel logLevel = config.getCurrentEnvironment().isAllowInsecurePublicKey() ? LogLevel.WARN : LogLevel.ERROR;
+            final LogLevel logLevel = config.isAllowInsecurePublicKey() ? LogLevel.WARN : LogLevel.ERROR;
             withoutX5CValidation().withErrorLogLevel(logLevel).build().validate(publicKey, purpose);
         } catch (final JWKValidationException exception) {
             return ValidationResult.error(exception);
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/RestEndpointBase.java b/core/src/test/java/dev/fitko/fitconnect/core/RestEndpointBase.java
index bfac3e87b8dad9262638afd2909c737449369a24..cb9c881bd61453693618846be13bd573afe95331 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/RestEndpointBase.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/RestEndpointBase.java
@@ -8,6 +8,7 @@ import dev.fitko.fitconnect.api.config.EnvironmentName;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
+import java.util.List;
 import java.util.Map;
 
 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
@@ -30,7 +31,7 @@ public abstract class RestEndpointBase {
     protected ApplicationConfig getTestConfig(final String fakeBaseUrl) {
 
         final var envName = new EnvironmentName("TESTING");
-        final var environments = Map.of(envName, new Environment(fakeBaseUrl, fakeBaseUrl, fakeBaseUrl, fakeBaseUrl, false, false));
+        final var environments = Map.of(envName, new Environment(fakeBaseUrl, fakeBaseUrl, List.of(fakeBaseUrl), fakeBaseUrl, false, false, false));
 
         return ApplicationConfig.builder()
                 .environments(environments)
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
index 0dbc9bd4f7c4d17b7b38cf90130b05879f2eb24c..27cb30f7e4b7d24dd02474a7aeac8fbc9496b154 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSenderTest.java
@@ -24,10 +24,10 @@ import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.EncryptionException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EncryptionException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.Sender;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
index fc81efa09817057194950517c9099c2d4c930558..7fc169b1cb0510431c6e94dcb8392c076f7c4455 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/SubmissionSubscriberTest.java
@@ -22,9 +22,9 @@ import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionsForPickup;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.DataIntegrityException;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.DataIntegrityException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.Subscriber;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogApiServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogApiServiceTest.java
index 9db27e1fb2cf685864cafca3c9e2e7b4a4dba616..c7d5a69ea88e2ae8b2061c20e6b2976e658f6df4 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogApiServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogApiServiceTest.java
@@ -10,7 +10,7 @@ import dev.fitko.fitconnect.api.domain.model.event.SubmissionState;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.events.EventLogService;
 import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
index 59a2478ce9b71de1d001108c71c84a17d4d64445..0a2592453ead7680ce2b368b2321e7757ea96016 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/EventLogVerifierTest.java
@@ -14,7 +14,7 @@ import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
index c90c7c7e3282d84ef71454e0ad88ac6816c13165..a86171c25e6377858ceacd345351bb59eef3faa3 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/events/SecurityEventTokenServiceTest.java
@@ -10,13 +10,13 @@ import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.config.Environment;
 import dev.fitko.fitconnect.api.config.EnvironmentName;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.AttachmentsMismatch;
 import dev.fitko.fitconnect.api.domain.model.submission.Submission;
 import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
-import dev.fitko.fitconnect.api.exceptions.EventCreationException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.api.services.events.SecurityEventService;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
@@ -261,10 +261,10 @@ class SecurityEventTokenServiceTest {
         final var envName = new EnvironmentName("testing");
         final var testing = new Environment();
         testing.setAllowInsecurePublicKey(true);
-        final var config = new ApplicationConfig();
-        config.setEnvironments(Map.of(envName, testing));
-        config.setActiveEnvironment(envName);
-        return config;
+        return ApplicationConfig.builder()
+                .activeEnvironment(envName)
+                .environments(Map.of(envName, testing))
+                .build();
     }
 
     private String getResourceAsString(final String name) throws IOException {
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
index 14d04f580160285895ff9a2f93fc344043fa6e7f..2f375e7c0a108973e7bfe65cd6b067f62863d4ee 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/keys/PublicKeyServiceTest.java
@@ -10,7 +10,7 @@ import dev.fitko.fitconnect.api.config.EnvironmentName;
 import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.InvalidKeyException;
+import dev.fitko.fitconnect.api.exceptions.internal.InvalidKeyException;
 import dev.fitko.fitconnect.api.services.auth.OAuthService;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.submission.SubmissionService;
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.text.ParseException;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -57,13 +58,14 @@ class PublicKeyServiceTest extends RestEndpointBase {
 
         final EnvironmentName envName = new EnvironmentName("TESTING");
         final Environment environment = new Environment();
-        environment.setSubmissionBaseUrl(baseUrl);
+        environment.setSubmissionBaseUrls(List.of(baseUrl));
         environment.setSelfServicePortalBaseUrl(baseUrl);
         environment.setAllowInsecurePublicKey(true);
 
-        final ApplicationConfig config = new ApplicationConfig();
-        config.setEnvironments(Map.of(envName, environment));
-        config.setActiveEnvironment(envName);
+        final ApplicationConfig config = ApplicationConfig.builder()
+                .environments(Map.of(envName, environment))
+                .activeEnvironment(envName)
+                .build();
 
         underTest = new PublicKeyService(config, new HttpClient(), authServiceMock, submissionServiceMock, validationServiceMock);
     }
@@ -115,12 +117,13 @@ class PublicKeyServiceTest extends RestEndpointBase {
 
         final EnvironmentName envName = new EnvironmentName("PROD");
         final Environment environment = new Environment();
-        environment.setSubmissionBaseUrl("http://localhost:" + wireMockServer.port());
+        environment.setSubmissionBaseUrls(List.of("http://localhost:" + wireMockServer.port()));
         environment.setAllowInsecurePublicKey(false);
 
-        final ApplicationConfig config = new ApplicationConfig();
-        config.setEnvironments(Map.of(envName, environment));
-        config.setActiveEnvironment(envName);
+        final ApplicationConfig config = ApplicationConfig.builder()
+                .environments(Map.of(envName, environment))
+                .activeEnvironment(envName)
+                .build();
 
         final PublicKeyService keyService = new PublicKeyService(config, new HttpClient(), authServiceMock, submissionServiceMock, validationServiceMock);
 
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/routing/RouteVerifierTest.java b/core/src/test/java/dev/fitko/fitconnect/core/routing/RouteVerifierTest.java
index 4b441543955a154e657bbe5b228cfb2d64d78c3e..02fb3557709bc7728e5cf30e3167ce630a707499 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/routing/RouteVerifierTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/routing/RouteVerifierTest.java
@@ -9,7 +9,7 @@ import com.nimbusds.jose.util.Base64URL;
 import com.nimbusds.jwt.SignedJWT;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
 import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
-import dev.fitko.fitconnect.api.exceptions.ValidationException;
+import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
 import dev.fitko.fitconnect.api.services.keys.KeyService;
 import dev.fitko.fitconnect.api.services.validation.ValidationService;
 import dev.fitko.fitconnect.core.SubmissionSenderTest;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/routing/RoutingApiServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/routing/RoutingApiServiceTest.java
index d489846033ca7a1725574365601ff4ac24c56f62..56b59e9443e75f756fdfdee486b0496a43a839dc 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/routing/RoutingApiServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/routing/RoutingApiServiceTest.java
@@ -9,7 +9,7 @@ import dev.fitko.fitconnect.api.domain.model.route.Area;
 import dev.fitko.fitconnect.api.domain.model.route.AreaResult;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
 import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
-import dev.fitko.fitconnect.api.exceptions.RestApiException;
+import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
 import dev.fitko.fitconnect.api.services.routing.RoutingService;
 import dev.fitko.fitconnect.core.RestEndpointBase;
 import dev.fitko.fitconnect.core.http.HttpClient;
@@ -44,9 +44,10 @@ public class RoutingApiServiceTest extends RestEndpointBase {
         final Environment environment = new Environment();
         environment.setRoutingBaseUrl(fakeBaseUrl);
 
-        final ApplicationConfig config = new ApplicationConfig();
-        config.setEnvironments(Map.of(envName, environment));
-        config.setActiveEnvironment(envName);
+        final ApplicationConfig config = ApplicationConfig.builder()
+                .environments(Map.of(envName, environment))
+                .activeEnvironment(envName)
+                .build();
 
         underTest = new RoutingApiService(config, new HttpClient());
     }
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java b/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
index 81034bf5e024a7e12b0eb2a49642ded906dc4e9b..89cecd8278fa2166ebfbc3a3d348ca2d8f689fb4 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/schema/SchemaResourceProviderTest.java
@@ -2,9 +2,9 @@ package dev.fitko.fitconnect.core.schema;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
-import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
+import dev.fitko.fitconnect.api.exceptions.internal.SchemaNotFoundException;
 import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
 import dev.fitko.fitconnect.core.http.HttpClient;
 import dev.fitko.fitconnect.core.http.HttpResponse;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java b/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
index 4ba6454ce2cc4e9b9fa5527b4f2fcb2037743d3a..41016b54c84e12d8c7b2fe88d67c7cc7063579dc 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/util/EventLogUtilTest.java
@@ -5,9 +5,8 @@ import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.EventIssuer;
 import dev.fitko.fitconnect.api.domain.model.event.EventLog;
 import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
-import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
-import dev.fitko.fitconnect.api.exceptions.EventLogException;
+import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
diff --git a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
index 512078accbb450843f6f32af43735057783866ae..38c1ee1e018e21bb190c481f9d797e39b7d0163f 100644
--- a/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
+++ b/core/src/test/java/dev/fitko/fitconnect/core/validation/DefaultValidationServiceTest.java
@@ -11,7 +11,7 @@ import com.nimbusds.jose.util.Base64URL;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.config.Environment;
 import dev.fitko.fitconnect.api.config.EnvironmentName;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.domain.model.destination.Destination;
 import dev.fitko.fitconnect.api.domain.model.destination.DestinationService;
 import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
@@ -152,13 +152,14 @@ class DefaultValidationServiceTest {
         //Given
         final String urlDummy = "https://localhost";
         final EnvironmentName envName = new EnvironmentName("DEV");
-        final Environment env = new Environment(urlDummy, urlDummy, urlDummy, urlDummy, false, false);
+        final Environment env = new Environment(urlDummy, urlDummy, List.of(urlDummy), urlDummy, false, false, false);
 
-        final var config = new ApplicationConfig();
-        config.setHttpProxyHost(urlDummy);
-        config.setHttpProxyPort(8080);
-        config.setEnvironments(Map.of(envName, env));
-        config.setActiveEnvironment(envName);
+        final var config = ApplicationConfig.builder()
+                .httpProxyHost(urlDummy)
+                .httpProxyPort(8080)
+                .environments(Map.of(envName, env))
+                .activeEnvironment(envName)
+                .build();
 
         final DefaultValidationService underTest = new DefaultValidationService(config, hashService, schemaProvider,
                 Collections.emptyList());
@@ -261,7 +262,7 @@ class DefaultValidationServiceTest {
         data.setSubmissionSchema(submissionSchema);
         data.setHash(hash);
 
-        final Hash  attachmentOneHash = new Hash();
+        final Hash attachmentOneHash = new Hash();
         attachmentOneHash.setContent(hashService.toHexString(hashService.createHash("someHash".getBytes())));
         attachmentOneHash.setSignatureType(SignatureType.SHA_512);
 
@@ -271,7 +272,7 @@ class DefaultValidationServiceTest {
         attachmentOne.setMimeType("application/pdf");
         attachmentOne.setHash(attachmentOneHash);
 
-        final Hash  attachmentTwoHash = new Hash();
+        final Hash attachmentTwoHash = new Hash();
         attachmentTwoHash.setContent(hashService.toHexString(hashService.createHash("someHash".getBytes())));
         attachmentTwoHash.setSignatureType(SignatureType.SHA_512);
 
@@ -586,7 +587,7 @@ class DefaultValidationServiceTest {
         // attachmentId that were announced
         submission.setAttachments(List.of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID()));
 
-        final Hash  attachmentHash = new Hash();
+        final Hash attachmentHash = new Hash();
         attachmentHash.setContent(hashService.toHexString(hashService.createHash("someHash".getBytes())));
         attachmentHash.setSignatureType(SignatureType.SHA_512);
 
@@ -1403,9 +1404,10 @@ class DefaultValidationServiceTest {
         final var testing = new Environment();
         testing.setAllowInsecurePublicKey(allowInsecureKey);
         testing.setSkipSubmissionDataValidation(skipSubmissionDataValidation);
-        final var config = new ApplicationConfig();
-        config.setEnvironments(Map.of(envName, testing));
-        config.setActiveEnvironment(envName);
+        final var config = ApplicationConfig.builder()
+                .environments(Map.of(envName, testing))
+                .activeEnvironment(envName)
+                .build();
         return config;
     }
 
diff --git a/client/src/test/java/dev/fitko/fitconnect/client/ClientIntegrationTest.java b/integration-tests/README.md
similarity index 100%
rename from client/src/test/java/dev/fitko/fitconnect/client/ClientIntegrationTest.java
rename to integration-tests/README.md
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/AuthenticationIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/AuthenticationIT.java
index 2037094728c6eac936749f7d13f2126b3281ba04..bee40f79b0dbedfa83a41fca4847a8ee831310de 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/AuthenticationIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/AuthenticationIT.java
@@ -1,5 +1,6 @@
 package dev.fitko.fitconnect.integrationtests;
 
+import dev.fitko.fitconnect.api.config.build.BuildInfo;
 import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
 import dev.fitko.fitconnect.core.auth.DefaultOAuthService;
 import dev.fitko.fitconnect.core.http.HttpClient;
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
index 3a9f129a0392d62325cc21662da4d24f27d037dc..ec7764c11d9ed60c22afca1bcf317760976e9df5 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/IntegrationTestBase.java
@@ -7,7 +7,7 @@ import dev.fitko.fitconnect.api.config.SenderConfig;
 import dev.fitko.fitconnect.api.config.SubscriberConfig;
 import dev.fitko.fitconnect.api.domain.model.event.problems.Problem;
 import dev.fitko.fitconnect.client.SubscriberClient;
-import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
 
 import java.io.IOException;
 import java.util.List;
@@ -50,7 +50,7 @@ public class IntegrationTestBase {
                 .build();
 
         final EnvironmentName envName = new EnvironmentName(environmentName);
-        final Environment env = new Environment(authBaseUrl, routingBaseUrl, submissionBaseUrl, selfServicePortalUrl, allowInsecurePublicKey, false);
+        final Environment env = new Environment(authBaseUrl, routingBaseUrl, List.of(submissionBaseUrl), selfServicePortalUrl, true, allowInsecurePublicKey, false);
 
         return ApplicationConfig.builder()
                 .senderConfig(sender)
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/RoutingIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/RoutingIT.java
index adab025fa0ceb0843011aa24cc7447d6cc72d05b..bdbd0b203874e58945075c6fd033b532a56495e1 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/RoutingIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/RoutingIT.java
@@ -3,8 +3,8 @@ package dev.fitko.fitconnect.integrationtests;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
 import dev.fitko.fitconnect.api.domain.model.route.Area;
 import dev.fitko.fitconnect.api.domain.model.route.Route;
-import dev.fitko.fitconnect.client.RoutingClient;
-import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.RouterClient;
+import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
 import dev.fitko.fitconnect.client.router.DestinationSearch;
 import dev.fitko.fitconnect.integrationtests.condition.EnableIfEnvironmentVariablesAreSet;
 import org.hamcrest.Matchers;
@@ -25,7 +25,7 @@ public class RoutingIT extends IntegrationTestBase {
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
-        final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+        final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
         final DestinationSearch search = DestinationSearch.Builder()
                 .withLeikaKey("99123456760610")
@@ -33,7 +33,7 @@ public class RoutingIT extends IntegrationTestBase {
                 .build();
 
         // When
-        final List<Route> routes = routingClient.findDestinations(search);
+        final List<Route> routes = routerClient.findDestinations(search);
 
         // Then
         assertThat(routes, Matchers.hasSize(1));
@@ -46,7 +46,7 @@ public class RoutingIT extends IntegrationTestBase {
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
-        final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+        final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
         final DestinationSearch search = DestinationSearch.Builder()
                 .withLeikaKey("99123456760610")
@@ -54,7 +54,7 @@ public class RoutingIT extends IntegrationTestBase {
                 .build();
 
         // When
-        final List<Route> routes = routingClient.findDestinations(search);
+        final List<Route> routes = routerClient.findDestinations(search);
 
         // Then
         assertThat(routes, Matchers.hasSize(1));
@@ -67,10 +67,10 @@ public class RoutingIT extends IntegrationTestBase {
         // Given
         final ApplicationConfig config = getConfigWithCredentialsFromEnvironment("TESTING", true);
 
-        final RoutingClient routingClient = ClientFactory.getRoutingClient(config);
+        final RouterClient routerClient = ClientFactory.getRouterClient(config);
 
         // When
-        final List<Area> areas = routingClient.findAreas(List.of("Leip*", "04229"), 0, 10);
+        final List<Area> areas = routerClient.findAreas(List.of("Leip*", "04229"), 0, 10);
 
         // Then
         assertThat(areas, Matchers.is(Matchers.not(Matchers.empty())));
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
index cb71cf46dfe838bce4863114e72ca74dff22c970..905c1b078b22bb97a3a829b459620a9161465a17 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SenderClientIT.java
@@ -3,7 +3,7 @@ package dev.fitko.fitconnect.integrationtests;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.nimbusds.jose.jwk.RSAKey;
 import dev.fitko.fitconnect.api.config.ApplicationConfig;
-import dev.fitko.fitconnect.api.config.SchemaConfig;
+import dev.fitko.fitconnect.api.config.defaults.SchemaConfig;
 import dev.fitko.fitconnect.api.domain.model.event.Event;
 import dev.fitko.fitconnect.api.domain.model.event.SubmissionStatus;
 import dev.fitko.fitconnect.api.domain.model.metadata.ContentStructure;
@@ -16,10 +16,11 @@ import dev.fitko.fitconnect.api.domain.model.metadata.data.Data;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.MimeType;
 import dev.fitko.fitconnect.api.domain.model.metadata.data.SubmissionSchema;
 import dev.fitko.fitconnect.api.domain.model.replychannel.ReplyChannel;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSenderException;
 import dev.fitko.fitconnect.api.services.crypto.CryptoService;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.SubscriberClient;
-import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.SendableEncryptedSubmission;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
@@ -47,9 +48,11 @@ import java.util.List;
 import java.util.UUID;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 @EnableIfEnvironmentVariablesAreSet
 public class SenderClientIT extends IntegrationTestBase {
@@ -184,10 +187,11 @@ public class SenderClientIT extends IntegrationTestBase {
                 .setJsonData(getResourceAsString("/submission_data.json"), URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                 .build();
 
-        final var sentSubmission = ClientFactory.getSenderClient(config).send(submission);
+        final FitConnectSenderException exception = assertThrows(FitConnectSenderException.class, () -> ClientFactory.getSenderClient(config).send(submission));
 
         // Then
-        Assertions.assertNull(sentSubmission);
+        assertThat(exception.getMessage(), containsString("Sending submission failed"));
+        assertThat(exception.getCause().getMessage(), containsString("JWK with id jsMQHFiA2uMGtiG3Ro8RJnYdEhp5W5KjGW-Vcf3-YMk has an invalid amount of certificates. Is 1 but should be 3."));
     }
 
     @Test
diff --git a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
index 71524ce6b326656e4b3efca8e51956e8769a0b88..dcb2d642aff6c6f1da4cbf8164364e16bbeaf8a0 100644
--- a/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
+++ b/integration-tests/src/test/java/dev/fitko/fitconnect/integrationtests/SubscriberClientIT.java
@@ -9,10 +9,10 @@ import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAu
 import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
 import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
 import dev.fitko.fitconnect.api.exceptions.RestApiException;
-import dev.fitko.fitconnect.api.exceptions.SubmissionRequestException;
+import dev.fitko.fitconnect.api.exceptions.client.FitConnectSubscriberException;
 import dev.fitko.fitconnect.client.SenderClient;
 import dev.fitko.fitconnect.client.SubscriberClient;
-import dev.fitko.fitconnect.client.factory.ClientFactory;
+import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
 import dev.fitko.fitconnect.client.sender.model.Attachment;
 import dev.fitko.fitconnect.client.sender.model.SendableSubmission;
 import dev.fitko.fitconnect.client.subscriber.ReceivedSubmission;
@@ -172,7 +172,7 @@ public class SubscriberClientIT extends IntegrationTestBase {
 
         // second attempt to receive and reject the submission should return an empty result
         // since the submission is gone after being rejected
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
         assertThat(exception.getCause(), instanceOf(RestApiException.class));
         assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
     }
@@ -212,7 +212,7 @@ public class SubscriberClientIT extends IntegrationTestBase {
 
         // second attempt to receive the submission should return an empty result
         // since the submission is gone after being accepted
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
         assertThat(exception.getCause(), instanceOf(RestApiException.class));
         assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
     }
@@ -252,7 +252,7 @@ public class SubscriberClientIT extends IntegrationTestBase {
 
         // second attempt to receive the submission should return an empty result
         // since the submission is gone after being accepted
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
         assertThat(exception.getCause(), instanceOf(RestApiException.class));
         assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
     }
@@ -295,7 +295,7 @@ public class SubscriberClientIT extends IntegrationTestBase {
         assertThat(senderClient.getStatusForSubmission(sentSubmission).getStatus(), is(SubmissionState.REJECTED));
 
         // second attempt to receive and reject the submission should return an empty result since the submission is gone after being rejected
-        final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> subscriberClient.requestSubmission(sentSubmission.getSubmissionId()));
+        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmission.getSubmissionId()));
         assertThat(exception.getCause(), instanceOf(RestApiException.class));
         assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
     }
diff --git a/pom.xml b/pom.xml
index b7fa9108f9936d4f8a8e293055b2a36455b5cfaf..2108a9c3e9f3d86dbdecb93aba5e3a53cc64127a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,8 +62,8 @@
         <!-- 3rd party dependencies -->
         <nimbus.version>9.31</nimbus.version>
         <okhttp.version>4.10.0</okhttp.version>
-        <jackson-databind.version>2.14.2</jackson-databind.version>
-        <jackson-annotations.version>2.14.2</jackson-annotations.version>
+        <jackson-databind.version>2.15.0</jackson-databind.version>
+        <jackson-annotations.version>2.15.0</jackson-annotations.version>
         <lombock.version>1.18.26</lombock.version>
         <logback.version>1.4.7</logback.version>
         <slf4j.version>2.0.7</slf4j.version>
@@ -95,7 +95,7 @@
         <nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version>
 
         <git-commit-id-maven-plugin.version>5.0.0</git-commit-id-maven-plugin.version>
-        <jacoco-maven-plugin.version>0.8.9</jacoco-maven-plugin.version>
+        <jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
 
         <local-version-suffix>-local</local-version-suffix>