diff --git a/ExpressAPI/assets/OpenAPI/OpenAPI.yaml b/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
index 1d945690c02269c9dbffe84e8264f30e45af74f9..566a57194de5ce25f63fe4a4b75c2151e0eae824 100644
--- a/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
+++ b/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
@@ -24,10 +24,12 @@ servers:
 tags:
     -   name: General
         description: ''
+    -   name: Sonar
+        description: Routes that are used to manage SonarQube information
     -   name: Session
         description: Routes that are used to manage the user's session
     -   name: Gitlab
-        description: Routes that are used to provide Gitlab informations
+        description: Routes that are used to provide Gitlab information
     -   name: Assignment
         description: Routes that are used to manage assignments
     -   name: Exercise
@@ -88,10 +90,10 @@ paths:
                     description: OK
                 default:
                     $ref: '#/components/responses/ERROR'
-    /sonar:
+    /sonar/info:
         get:
             tags:
-                - General
+                - Sonar
             summary: Check sonar status
             description: This route can be used to check if the server supports sonar and if the integration is enabled.
             responses:
@@ -120,6 +122,55 @@ paths:
                     description: OK
                 default:
                     $ref: '#/components/responses/ERROR'
+    /sonar/testqualities:
+        post:
+            tags:
+                - Sonar
+            summary: Test existence and validity of a quality gate and quality profiles
+            description: |
+                This route should be used at assignment creation to test existence and validity of a quality gate and quality profiles before creating the assignment
+                **🔒 Security needs:** TeachingStaff or Admin roles
+            security:
+                -   Clients_Token: [ ]
+            requestBody:
+                content:
+                    multipart/form-data:
+                        schema:
+                            type: object
+                            properties:
+                                gate:
+                                    type: string
+                                profiles:
+                                    type: string
+                                    format: json
+                                    description: JSON string array of quality profiles
+                            required:
+                                - gate
+                                - profiles
+            responses:
+                '200':
+                    content:
+                        application/json:
+                            schema:
+                                allOf:
+                                    -   $ref: '#/components/schemas/DojoBackendResponse'
+                                    -   type: object
+                                        properties:
+                                            data:
+                                                type: object
+                                                properties:
+                                                    valid:
+                                                        type: boolean
+                                                    badGate:
+                                                        type: string
+                                                        description: Name of the gate if invalid, or null
+                                                    badProfiles:
+                                                        type: array
+                                                        items: string
+                                                        description: List of invalid profiles
+                    description: OK
+                default:
+                    $ref: '#/components/responses/ERROR'
     /login:
         post:
             tags:
diff --git a/ExpressAPI/src/managers/SonarManager.ts b/ExpressAPI/src/managers/SonarManager.ts
index 9c8946ecaaa17cf974934a717ed613d58a0344c2..ed082834c805dc2077645f9d5c8793361aa63b8f 100644
--- a/ExpressAPI/src/managers/SonarManager.ts
+++ b/ExpressAPI/src/managers/SonarManager.ts
@@ -47,11 +47,13 @@ class SonarManager {
         })).data;
     }
 
-    private async executeGetRequest<T>(url: string) {
+    private async executeGetRequest<T>(url: string, data?: unknown) {
+
         return (await this.instance.get<T>(url, {
             headers: {
                 Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }`
-            }
+            },
+            params: data
         })).data;
     }
 
@@ -128,6 +130,31 @@ class SonarManager {
         const resp = await this.executeGetRequest<{ languages: { key: string, name: string }[]}>(this.getApiUrl(SonarRoute.GET_LANGUAGES))
         return resp.languages.map(l => l.key)
     }
+
+    async testQualityGate(gateName: string) {
+        try {
+            await this.executeGetRequest(this.getApiUrl(SonarRoute.TEST_GATE), { name: gateName });
+            return true;
+        } catch ( e ) {
+            return false;
+        }
+    }
+
+    async testQualityProfile(profileName: string, language: string) {
+        try {
+            const formData = new FormData();
+            formData.append('language', language);
+            formData.append('qualityProfile', profileName);
+
+            const resp = await this.executeGetRequest<{ profiles: { key: string, name: string, language: string }[] }>(
+                this.getApiUrl(SonarRoute.TEST_PROFILE), formData
+            );
+
+            return (resp.profiles.length > 0 && resp.profiles.some(p => p.name === profileName && p.language === language))
+        } catch ( e ) {
+            return false;
+        }
+    }
 }
 
 export default new SonarManager();
\ No newline at end of file
diff --git a/ExpressAPI/src/routes/ApiRoutesManager.ts b/ExpressAPI/src/routes/ApiRoutesManager.ts
index 1110c107f068052a254b5c3e1c55c681c2b9f461..d8bad0314a78847b9a6c65d5faa0561536830246 100644
--- a/ExpressAPI/src/routes/ApiRoutesManager.ts
+++ b/ExpressAPI/src/routes/ApiRoutesManager.ts
@@ -7,6 +7,7 @@ import GitlabRoutes     from './GitlabRoutes.js';
 import ExerciseRoutes   from './ExerciseRoutes.js';
 import TagsRoutes       from './TagRoutes';
 import UserRoutes       from './UserRoutes';
+import SonarRoutes      from './SonarRoutes';
 
 
 class AdminRoutesManager implements RoutesManager {
@@ -18,6 +19,7 @@ class AdminRoutesManager implements RoutesManager {
         ExerciseRoutes.registerOnBackend(backend);
         TagsRoutes.registerOnBackend(backend);
         UserRoutes.registerOnBackend(backend);
+        SonarRoutes.registerOnBackend(backend);
     }
 }
 
diff --git a/ExpressAPI/src/routes/SonarRoutes.ts b/ExpressAPI/src/routes/SonarRoutes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..50d7a924ef14eda39b9c7fdae2a07e4e0fb38e69
--- /dev/null
+++ b/ExpressAPI/src/routes/SonarRoutes.ts
@@ -0,0 +1,82 @@
+import { Express }     from 'express-serve-static-core';
+import express         from 'express';
+import { StatusCodes } from 'http-status-codes';
+import RoutesManager   from '../express/RoutesManager';
+import SharedSonarManager from '../shared/managers/SharedSonarManager';
+import SonarManager from '../managers/SonarManager';
+import SecurityMiddleware from '../middlewares/SecurityMiddleware';
+import SecurityCheckType from '../types/SecurityCheckType';
+import * as ExpressValidator from 'express-validator';
+import DojoValidators from '../helpers/DojoValidators';
+import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware';
+
+class SonarRoutes implements RoutesManager {
+    private readonly qualitiesValidator: ExpressValidator.Schema = {
+        gate    : {
+            trim    : true,
+            notEmpty: false
+        },
+        profiles : {
+            trim           : true,
+            notEmpty       : false,
+            customSanitizer: DojoValidators.jsonSanitizer
+        }
+    };
+
+    registerOnBackend(backend: Express) {
+        backend.get('/sonar/info', this.sonar.bind(this));
+        backend.post('/sonar/testqualities', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.qualitiesValidator), this.testQualities.bind(this));
+    }
+
+    private async sonar(req: express.Request, res: express.Response) {
+        const data = {
+            sonarEnabled: await SharedSonarManager.isSonarSupported(),
+            languages: await SonarManager.getLanguages()
+        };
+        return req.session.sendResponse(res, StatusCodes.OK, data);
+    }
+
+    private async testQualities(req: express.Request, res: express.Response) {
+        const params: {
+            gate: string | undefined, profiles: string[]
+        } = req.body;
+
+        console.log(params);
+
+        let gateOk = true;
+        if ((params.gate ?? "") !== "") {
+            gateOk = await SonarManager.testQualityGate(params.gate ?? "")
+        }
+
+        let profilesOk = true;
+        const badProfiles = [];
+
+        for ( const profile of params.profiles ) {
+            try {
+                const [ lang, name ] = profile.trim().split('/');
+                if ( !await SonarManager.testQualityProfile(name, lang) ) {
+                    profilesOk = false;
+                    badProfiles.push(profile);
+                }
+            } catch (e) {
+                profilesOk = false;
+                badProfiles.push(profile);
+            }
+        }
+
+        console.log(gateOk, profilesOk);
+
+        const data = {
+            valid: gateOk && profilesOk,
+            badProfiles: badProfiles,
+            badGate: (gateOk ? null : params.gate)
+        };
+
+        console.log(data);
+
+        return req.session.sendResponse(res, StatusCodes.OK, data);
+    }
+}
+
+
+export default new SonarRoutes();