diff --git a/Jenkinsfile b/Jenkinsfile
index 29d72c6ab83a5b3d97f1bd01d6ada52ad2dc0f2a..a4c66b8630e7fc3fe05d4887c3a78b333740cbaa 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -48,7 +48,7 @@ pipeline {
                 }
 
                 configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
-                    sh "mvn -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3 -DelasticTests.disabled=true -Dbuild.number=$BUILD_NUMBER"
+                    sh 'mvn -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3 -DelasticTests.disabled=true -Dbuild.number=$BUILD_NUMBER'
                 }
             }
         }
@@ -116,7 +116,7 @@ pipeline {
                 
                 configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
                     withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
-                        sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD}'
+                        sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER'
                     }
                 }
             }
diff --git a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
index cd66647d10b09a38b229b1931cf2e65bbef5733a..2cf01448f6fa1ab4f52d3a72fc06b15d57c5cda7 100644
--- a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
+++ b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
@@ -28,13 +28,11 @@ import org.springframework.hateoas.server.RepresentationModelAssembler;
 import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
 import org.springframework.stereotype.Component;
 
-import io.micrometer.common.lang.NonNullApi;
 import lombok.RequiredArgsConstructor;
 
 @Component
 @RequiredArgsConstructor
-@NonNullApi
-class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> {
+public class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> {
 	static final String REL_CONFIGURATION = "configuration";
 
 	private final RepositoryRestProperties restProperties;
@@ -46,7 +44,6 @@ class RootModelAssembler implements RepresentationModelAssembler<Root, EntityMod
 		return EntityModel.of(
 				root,
 				Link.of(configLink.toUriString(), REL_CONFIGURATION),
-				rootLink.withSelfRel()
-		);
+				rootLink.withSelfRel());
 	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandler.java b/src/main/java/de/ozgcloud/admin/common/errorhandling/ExceptionController.java
similarity index 95%
rename from src/main/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandler.java
rename to src/main/java/de/ozgcloud/admin/common/errorhandling/ExceptionController.java
index 27f9c626b4586717558355e5a2671676ec0e570d..75a3dda5ce10f4d50ebaaabc31ec840ac9c54d3b 100644
--- a/src/main/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandler.java
+++ b/src/main/java/de/ozgcloud/admin/common/errorhandling/ExceptionController.java
@@ -19,7 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.errorhandling;
+package de.ozgcloud.admin.common.errorhandling;
 
 import java.util.Map;
 
@@ -36,7 +36,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep
 import de.ozgcloud.common.errorhandling.TechnicalException;
 
 @RestControllerAdvice
-public class AdminExceptionHandler extends ResponseEntityExceptionHandler {
+public class ExceptionController extends ResponseEntityExceptionHandler {
 
 	static final Map<Class<? extends Exception>, HttpStatus> STATUS_BY_EXCEPTION = Map.of(
 			RuntimeException.class, HttpStatus.INTERNAL_SERVER_ERROR,
diff --git a/src/main/java/de/ozgcloud/admin/errorhandling/FunctionalException.java b/src/main/java/de/ozgcloud/admin/common/errorhandling/FunctionalException.java
similarity index 97%
rename from src/main/java/de/ozgcloud/admin/errorhandling/FunctionalException.java
rename to src/main/java/de/ozgcloud/admin/common/errorhandling/FunctionalException.java
index e0e57b3feb65073674c0645aac9fc997af8dbfed..5b2369b2a97772a1607b84ebaa9a46de2fec9643 100644
--- a/src/main/java/de/ozgcloud/admin/errorhandling/FunctionalException.java
+++ b/src/main/java/de/ozgcloud/admin/common/errorhandling/FunctionalException.java
@@ -19,7 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.errorhandling;
+package de.ozgcloud.admin.common.errorhandling;
 
 import java.io.Serial;
 import java.util.UUID;
diff --git a/src/main/java/de/ozgcloud/admin/environment/FrontendEnvironment.java b/src/main/java/de/ozgcloud/admin/environment/Environment.java
similarity index 87%
rename from src/main/java/de/ozgcloud/admin/environment/FrontendEnvironment.java
rename to src/main/java/de/ozgcloud/admin/environment/Environment.java
index 0f7ca897f9e77c5956cdcab164977ea35d694476..e2bfb54b4aeb2d33da4461d3eb6ae95d89eab93e 100644
--- a/src/main/java/de/ozgcloud/admin/environment/FrontendEnvironment.java
+++ b/src/main/java/de/ozgcloud/admin/environment/Environment.java
@@ -5,7 +5,7 @@ import lombok.Getter;
 
 @Getter
 @Builder
-public class FrontendEnvironment {
+public class Environment {
 	private boolean production;
 	private String remoteHost;
 	private String authServer;
diff --git a/src/main/java/de/ozgcloud/admin/environment/FrontendEnvironmentController.java b/src/main/java/de/ozgcloud/admin/environment/EnvironmentController.java
similarity index 81%
rename from src/main/java/de/ozgcloud/admin/environment/FrontendEnvironmentController.java
rename to src/main/java/de/ozgcloud/admin/environment/EnvironmentController.java
index b61f1d9c358751a1e974be75b9cc33b3f2551536..71cf34ce3180c793235693f7ec8582a06aa27c63 100644
--- a/src/main/java/de/ozgcloud/admin/environment/FrontendEnvironmentController.java
+++ b/src/main/java/de/ozgcloud/admin/environment/EnvironmentController.java
@@ -9,10 +9,10 @@ import org.springframework.web.bind.annotation.RestController;
 import de.ozgcloud.admin.RootController;
 import lombok.RequiredArgsConstructor;
 
-@RestController
+@RestController("ozgCloudEnvironmentController")
 @RequiredArgsConstructor
-@RequestMapping(FrontendEnvironmentController.PATH)
-public class FrontendEnvironmentController {
+@RequestMapping(EnvironmentController.PATH)
+public class EnvironmentController {
 
 	static final String PATH = "/api/environment"; // NOSONAR
 
@@ -21,8 +21,8 @@ public class FrontendEnvironmentController {
 	private final OAuth2Properties oAuthProperties;
 
 	@GetMapping
-	public FrontendEnvironment getEnvironment() {
-		return FrontendEnvironment.builder()
+	public Environment getEnvironment() {
+		return Environment.builder()
 				.production(environmentProperties.isProduction())
 				.remoteHost(linkTo(RootController.class).toUri().toString())
 				.authServer(oAuthProperties.getAuthServerUrl())
diff --git a/src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerITCase.java b/src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4884dd0e4b23e908645251677c76064c9086e8a
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerITCase.java
@@ -0,0 +1,111 @@
+package de.ozgcloud.admin.common.errorhandling;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.util.Set;
+import java.util.stream.Stream;
+
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.constraints.NotEmpty;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+
+import de.ozgcloud.admin.RootController;
+import de.ozgcloud.admin.RootModelAssembler;
+import de.ozgcloud.common.test.ITCase;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.SneakyThrows;
+
+@ITCase
+@AutoConfigureMockMvc
+@WithMockUser
+class ExceptionControllerITCase {
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@MockBean
+	private RootModelAssembler modelAssembler;
+
+	@Nested
+	class TestExceptions {
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldHandleExceptionWithStatus(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			when(modelAssembler.toModel(any())).thenThrow(TestErrorController.EXCEPTION_PRODUCER.get(exceptionClass).produceException());
+
+			var result = performGet();
+
+			result.andExpect(status().is(expectedStatus.value()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldRespondWithStatusInBody(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			when(modelAssembler.toModel(any())).thenThrow(exceptionClass);
+
+			var result = performGet();
+
+			result.andExpect(jsonPath("$.status").value(expectedStatus.value()));
+		}
+
+		private static Stream<Arguments> exceptionAndExpectedStatus() {
+			return ExceptionController.STATUS_BY_EXCEPTION.entrySet().stream().map(kv -> Arguments.of(kv.getKey(), kv.getValue()));
+		}
+
+	}
+
+	@Nested
+	class TestConstraintViolationException {
+		@Test
+		@SneakyThrows
+		void shouldHaveValidationMessage() {
+			when(modelAssembler.toModel(any())).thenAnswer((a) -> {
+				throw new ConstraintViolationException(getConstraintViolations());
+			});
+
+			var result = performGet();
+
+			result.andExpect(jsonPath("$.detail").value("string: Empty field"));
+		}
+
+		private Set<ConstraintViolation<ValidatedClass>> getConstraintViolations() {
+			Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+			return validator.validate(ValidatedClass.builder().build());
+		}
+
+		@Getter
+		@Builder
+		private static class ValidatedClass {
+			@NotEmpty(message = "Empty field")
+			private String string;
+		}
+
+	}
+
+	@SneakyThrows
+	private ResultActions performGet() {
+		return mockMvc.perform(get(RootController.PATH));
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java b/src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerTest.java
similarity index 69%
rename from src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java
rename to src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerTest.java
index 646d2c0b24579a6d7cfd502fbbdf58cfc530806a..f33a3715b32778a78b725e096ea0bf99e1fd4047 100644
--- a/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java
+++ b/src/test/java/de/ozgcloud/admin/common/errorhandling/ExceptionControllerTest.java
@@ -19,9 +19,9 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.errorhandling;
+package de.ozgcloud.admin.common.errorhandling;
 
-import static de.ozgcloud.admin.errorhandling.AdminExceptionHandler.*;
+import static de.ozgcloud.admin.common.errorhandling.ExceptionController.*;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
@@ -41,7 +41,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
 import lombok.SneakyThrows;
 
-class AdminExceptionHandlerITCase {
+class ExceptionControllerTest {
 
 	private MockMvc mockMvc;
 
@@ -49,7 +49,7 @@ class AdminExceptionHandlerITCase {
 	void setup() {
 		mockMvc = MockMvcBuilders
 				.standaloneSetup(new TestErrorController())
-				.setControllerAdvice(new AdminExceptionHandler()).build();
+				.setControllerAdvice(new ExceptionController()).build();
 	}
 
 	@DisplayName("Error handler")
@@ -74,13 +74,49 @@ class AdminExceptionHandlerITCase {
 			result.andExpect(jsonPath("$.status").value(expectedStatus.value()));
 		}
 
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldRespondWithTitler(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			var result = doPerformWithError(exceptionClass);
+
+			result.andExpect(jsonPath("$.title").exists());
+		}
+
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldRespondWithDetail(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			var result = doPerformWithError(exceptionClass);
+
+			result.andExpect(jsonPath("$.detail").exists());
+		}
+
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldRespondWithInstance(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			var result = doPerformWithError(exceptionClass);
+
+			result.andExpect(jsonPath("$.instance").exists());
+		}
+
+		@ParameterizedTest
+		@MethodSource("exceptionAndExpectedStatus")
+		@SneakyThrows
+		void shouldRespondWithType(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) {
+			var result = doPerformWithError(exceptionClass);
+
+			result.andExpect(jsonPath("$.type").exists());
+		}
+
 		private static Stream<Arguments> exceptionAndExpectedStatus() {
 			return STATUS_BY_EXCEPTION.entrySet().stream().map(kv -> Arguments.of(kv.getKey(), kv.getValue()));
 		}
 
 		@SneakyThrows
 		private ResultActions doPerformWithError(Class<? extends Exception> exceptionClass) {
-			return mockMvc.perform(get("/test-error").param("errorClassName", exceptionClass.getName()));
+			return mockMvc.perform(get("/api/test-error").param("errorClassName", exceptionClass.getName()));
 		}
 	}
 
diff --git a/src/test/java/de/ozgcloud/admin/errorhandling/TestErrorController.java b/src/test/java/de/ozgcloud/admin/common/errorhandling/TestErrorController.java
similarity index 92%
rename from src/test/java/de/ozgcloud/admin/errorhandling/TestErrorController.java
rename to src/test/java/de/ozgcloud/admin/common/errorhandling/TestErrorController.java
index 4ad218f842cdc6e96a0c3b563e51aad7c9cdbeaf..c10446041aaba51ac761ac4e91ec01d6f9b7bb4b 100644
--- a/src/test/java/de/ozgcloud/admin/errorhandling/TestErrorController.java
+++ b/src/test/java/de/ozgcloud/admin/common/errorhandling/TestErrorController.java
@@ -19,7 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.errorhandling;
+package de.ozgcloud.admin.common.errorhandling;
 
 import java.util.Collections;
 import java.util.Map;
@@ -34,11 +34,9 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import de.ozgcloud.common.errorhandling.TechnicalException;
-import io.micrometer.common.lang.NonNullApi;
 
 @RestController
-@RequestMapping("/test-error")
-@NonNullApi
+@RequestMapping("/api/test-error")
 class TestErrorController {
 
 	@FunctionalInterface
@@ -54,13 +52,12 @@ class TestErrorController {
 			ConstraintViolationException.class, () -> new ConstraintViolationException(ERROR_MESSAGE, Collections.emptySet()),
 			ResourceNotFoundException.class, () -> new ResourceNotFoundException(ERROR_MESSAGE),
 			FunctionalException.class, () -> new FunctionalException(() -> ERROR_MESSAGE),
-			TechnicalException.class, () -> new TechnicalException(ERROR_MESSAGE)
-	);
+			TechnicalException.class, () -> new TechnicalException(ERROR_MESSAGE));
 
 	@GetMapping
 	String throwException(@RequestParam String errorClassName) throws Exception {
 		throw EXCEPTION_PRODUCER.get(
-				Class.forName(errorClassName)
-		).produceException();
+				Class.forName(errorClassName)).produceException();
 	}
+
 }
diff --git a/src/test/java/de/ozgcloud/admin/environment/FrontendEnvironmentControllerTest.java b/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java
similarity index 94%
rename from src/test/java/de/ozgcloud/admin/environment/FrontendEnvironmentControllerTest.java
rename to src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java
index 67d81f084a8a4bea8e747713cbb205c9d9433ca3..bdeaf83b00de2410112833f1447e940fc088e9b5 100644
--- a/src/test/java/de/ozgcloud/admin/environment/FrontendEnvironmentControllerTest.java
+++ b/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java
@@ -22,11 +22,11 @@ import de.ozgcloud.admin.RootController;
 import lombok.SneakyThrows;
 
 @ExtendWith(MockitoExtension.class)
-class FrontendEnvironmentControllerTest {
+class EnvironmentControllerTest {
 
 	@Spy
 	@InjectMocks
-	private FrontendEnvironmentController controller;
+	private EnvironmentController controller;
 
 	@Mock
 	private ProductionProperties environmentProperties;
@@ -120,7 +120,7 @@ class FrontendEnvironmentControllerTest {
 
 		@SneakyThrows
 		private ResultActions doRequest() {
-			return mockMvc.perform(get(FrontendEnvironmentController.PATH));
+			return mockMvc.perform(get(EnvironmentController.PATH));
 		}
 
 	}