diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js
index 829ef5dc8a47c4b9f0a42bfd810faa383c070bff..d0237fd453ea81093cc4943a2ed7ddec099ff0a2 100644
--- a/src/mocks/handlers.js
+++ b/src/mocks/handlers.js
@@ -10,6 +10,8 @@ import MockUsers from './mock_users.json';
 import MockSimulations from './mock_simulations.json';
 import MockUserGroups from './mock_user-groups.json';
 import MockGDPR from './mock_gdpr.json';
+import MockModels from './mock_models.json';
+import MockCustomModels from './mock_custom_models.json';
 
 import ImageAI from '../assets/images/Artificial_Intelligence_2.jpg';
 
@@ -58,6 +60,27 @@ export const handlers = [
   }),
   rest.post(`${config.api.proxy.url}${endpoints.proxy.identity.url}${endpoints.proxy.identity.gdpr.url}`,
     (req, res, ctx) => {
-      return res(ctx.json({'status':'success'}));
-    })
+      return res(ctx.json({ 'status': 'success' }));
+    }),
+  rest.get(`${config.api.proxy.url}${endpoints.proxy.models.url}/:modelType`,
+    (req, res, ctx) => {
+      return res(ctx.json(MockModels[0]));
+    }),
+  rest.post(`${config.api.proxy.url}${endpoints.proxy.models.url}/:modelType/:modelName`,
+    (req, res, ctx) => {
+      return res(ctx.json(MockCustomModels[2]));
+    }),
+  rest.get(`${config.api.proxy.url}${endpoints.proxy.storage.allCustomModels.url}/:modelType`,
+    (req, res, ctx) => {
+      return res(ctx.json(MockCustomModels[0]));
+    }),
+  rest.get(`${config.api.proxy.url}${endpoints.proxy.storage.userModels.url}/:modelType`,
+    (req, res, ctx) => {
+      return res(ctx.json(MockCustomModels[0]));
+    }),
+  rest.delete(`${config.api.proxy.url}${endpoints.proxy.storage.userModels.url}/:modelType/:modelName`,
+    (req, res, ctx) => {
+      return res(ctx.json(MockCustomModels[1]));
+    }),
+
 ];
\ No newline at end of file
diff --git a/src/mocks/mock_custom_models.json b/src/mocks/mock_custom_models.json
new file mode 100644
index 0000000000000000000000000000000000000000..3b312f522b3653e213da73a590223b99935ae14f
--- /dev/null
+++ b/src/mocks/mock_custom_models.json
@@ -0,0 +1,20 @@
+[
+    {
+        "name": "custom_hbp_clearpath_robotics_husky_a200",
+        "path": "robots/husky_model.zip",
+        "ownerName": "nrpuser",
+        "type": "robots",
+        "fileName": "husky_model.zip"
+    },
+    {
+        "name": "deleted_hbp_clearpath_robotics_husky_a200",
+        "ownerName": "nrpuser",
+        "type": "robots"
+    },
+    {
+        "name": "created_hbp_clearpath_robotics_husky_a200",
+        "ownerName": "nrpuser",
+        "type": "robots"
+    }
+
+]
\ No newline at end of file
diff --git a/src/mocks/mock_models.json b/src/mocks/mock_models.json
new file mode 100644
index 0000000000000000000000000000000000000000..2f2905b11a72d9128392831dcb72473bb215d1f4
--- /dev/null
+++ b/src/mocks/mock_models.json
@@ -0,0 +1,9 @@
+[
+    {
+        "name": "hbp_clearpath_robotics_husky_a200",
+        "path": "robots/husky_model.zip",
+        "ownerName": "nrpuser",
+        "type": "robots",
+        "fileName": "husky_model.zip"
+    }
+]
\ No newline at end of file
diff --git a/src/services/__tests__/models-storage-service.test.js b/src/services/__tests__/models-storage-service.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..94603b8af20db8b9813028d0c8209fd8aaa0162c
--- /dev/null
+++ b/src/services/__tests__/models-storage-service.test.js
@@ -0,0 +1,70 @@
+/**
+ * @jest-environment jsdom
+*/
+import '@testing-library/jest-dom';
+import 'jest-fetch-mock';
+
+import  ModelsStorageService from '../models/models-storage-service';
+
+test('makes sure that invoking the constructor fails with the right message', () => {
+  expect(() => {
+    new ModelsStorageService();
+  }).toThrow(Error);
+  expect(() => {
+    new ModelsStorageService();
+  }).toThrowError(Error('Use ModelsStorageService.instance'));
+});
+
+test('the service instance always refers to the same object', () => {
+  const instance1 = ModelsStorageService.instance;
+  const instance2 = ModelsStorageService.instance;
+  expect(instance1).toBe(instance2);
+});
+
+
+test('getTemplateModels function', async () => {
+  
+  let modelsService = ModelsStorageService.instance;
+
+  // fetch template robots
+  let response = await modelsService.getTemplateModels(true,'robots',false);
+  expect(response.name).toBe('hbp_clearpath_robotics_husky_a200');
+  expect(response.ownerName).toBe('nrpuser');
+  expect(response.type).toBe('robots');
+
+  // fetch custom robots
+  response = await modelsService.getTemplateModels(true,'robots',true);
+  expect(response.name).toBe('custom_hbp_clearpath_robotics_husky_a200');
+  expect(response.ownerName).toBe('nrpuser');
+  expect(response.type).toBe('robots'); 
+});
+
+test('getCustomModelsByUser function', async () => {
+  
+  let modelsService = ModelsStorageService.instance;
+
+  // fetch template robots
+  let response = await modelsService.getCustomModelsByUser('robots');
+  expect(response.name).toBe('custom_hbp_clearpath_robotics_husky_a200');
+  expect(response.ownerName).toBe('nrpuser');
+  expect(response.type).toBe('robots');
+
+});
+
+test('verifyModelType function', async () => {
+  
+  let modelsService = ModelsStorageService.instance;
+ const expectedErrorPart = 'Error Type 400: Bad Request : The model type notRobots';
+  // fetch template robots
+  expect(()=>modelsService.verifyModelType('notRobots')).toThrowError(expectedErrorPart);
+ 
+});
+
+test('setCustomModel function', async () => {
+  let modelsService = ModelsStorageService.instance;
+  
+  let response = await modelsService.setCustomModel('robots','husky','fakeContent');
+  expect(response.name).toBe('created_hbp_clearpath_robotics_husky_a200');
+  expect(response.ownerName).toBe('nrpuser');
+  expect(response.type).toBe('robots'); 
+});
diff --git a/src/services/models/models-storage-service.js b/src/services/models/models-storage-service.js
new file mode 100644
index 0000000000000000000000000000000000000000..342de10d94e3f089cdd8a49969fb5b4c655d7928
--- /dev/null
+++ b/src/services/models/models-storage-service.js
@@ -0,0 +1,146 @@
+import { HttpService } from '../http-service.js';
+
+import endpoints from '../proxy/data/endpoints.json';
+import config from '../../config.json';
+import ErrorHandlerService from '../error-handler-service';
+
+const storageModelsURL = `${config.api.proxy.url}${endpoints.proxy.models.url}`;
+const allCustomModelsURL = `${config.api.proxy.url}${endpoints.proxy.storage.allCustomModels.url}`;
+const userModelsURL = `${config.api.proxy.url}${endpoints.proxy.storage.userModels.url}`;
+
+let _instance = null;
+const SINGLETON_ENFORCER = Symbol();
+const availableModels = ['robots', 'brains', 'environments'];
+/**
+ * Service that manages the fetching and setting of custom and template
+ * models from the proxy.
+ */
+class ModelsStorageService extends HttpService {
+  constructor(enforcer) {
+    super();
+    if (enforcer !== SINGLETON_ENFORCER) {
+      throw new Error('Use ' + this.constructor.name + '.instance');
+    }
+  }
+
+  static get instance() {
+    if (_instance == null) {
+      _instance = new ModelsStorageService(SINGLETON_ENFORCER);
+    }
+
+    return _instance;
+  }
+
+  /**
+   * Retrieves the list of template or custom models from the proxy and stores
+   * them in the models class property. If the models are already
+   * there it just returns them, else does an HTTP request.
+   *
+   * @param {boolean} forceUpdate forces an update of the list
+   * @param {string} modelType one of the types
+   *                           ['robots', 'brains', 'environments']
+   * @param {boolean} allCustomModels if true fetch custom(user) models intead of templates
+   * @return models - the list of template models
+   */
+  async getTemplateModels(forceUpdate = false, modelType, allCustomModels = false) {
+    if (!this.models || forceUpdate) {
+      try {
+        this.verifyModelType(modelType);
+      }
+      catch (error) {
+        ErrorHandlerService.instance.dataError(error);
+      }
+
+      try {
+        const modelsWithTypeURL = allCustomModels ?
+          `${allCustomModelsURL}/${modelType}` :
+          `${storageModelsURL}/${modelType}`;
+        this.models = await (await this.httpRequestGET(modelsWithTypeURL)).json();
+      }
+      catch (error) {
+        ErrorHandlerService.instance.networkError(error);
+      }
+
+    }
+
+    return this.models;
+  }
+
+  /**
+   * Retrieves the list of custom models per user from the storage
+   *
+   * @param {string} modelType one of the types
+   *                           ['robots', 'brains', 'environments']
+   * @return models - the list of custom user models
+   */
+  async getCustomModelsByUser(modelType) {
+    try {
+      this.verifyModelType(modelType);
+      const customModelsURL = `${userModelsURL}/${modelType}`;
+      return (await this.httpRequestGET(customModelsURL)).json();
+    }
+    catch (error) {
+      ErrorHandlerService.instance.networkError(error);
+    }
+  }
+
+  /**
+   * Helper function that checks whether a specific type
+   * of model is in the list of available models.
+   *
+   * @param {string} modelType one of the types
+   *                           ['robots', 'brains', 'environments']
+   */
+  verifyModelType(modelType) {
+    if (!availableModels.includes(modelType)) {
+      throw new Error(
+        `Error Type 400: Bad Request : The model type ${modelType}
+        type that was requested is not one of brains, robots, environments.`);
+    }
+  }
+
+  /**
+    * Deletes a custom model from the storage
+    *
+    * @param {string} modelType one of the types
+    *                           ['robots', 'brains', 'environments']
+    * @param {string} modelName the name of the model to delete
+    * @return the response of the request
+    */
+
+  async deleteCustomModel(modelType, modelName) {
+    try {
+      this.verifyModelType(modelType);
+      const deleteCustomModelURL = `${userModelsURL}/${modelType}/${modelName}`;
+      return (await this.httpRequestDELETE(deleteCustomModelURL)).json();
+    }
+    catch (error) {
+      ErrorHandlerService.instance.dataError(error);
+    }
+  }
+
+  /**
+    * Sets a custom model to the storage
+    *
+    * @param {string} modelType one of the types
+    *                           ['robots', 'brains', 'environments']
+    * @param {string} modelName the name of the model to delete
+    * @param fileContent the data of the model to upload
+    * @return the response of the request
+    */
+  async setCustomModel(modelType, modelName, fileContent) {
+    
+    try {
+      this.verifyModelType(modelType);
+      const setCustomModelURL = `${storageModelsURL}/${modelType}/${modelName}`;
+      console.log(setCustomModelURL)
+      return (await this.httpRequestPOST(setCustomModelURL, fileContent)).json();
+    }
+    catch (error) {
+      ErrorHandlerService.instance.networkError(error);
+    }
+  }
+}
+
+
+export default ModelsStorageService;
diff --git a/src/services/proxy/data/endpoints.json b/src/services/proxy/data/endpoints.json
index aa5c143975c1d800ae711fe54b13a30784c8122c..a2c1acd09cb0a2cd4a9ad8429d89db43003fe87c 100644
--- a/src/services/proxy/data/endpoints.json
+++ b/src/services/proxy/data/endpoints.json
@@ -9,6 +9,9 @@
         "experiments": {
             "url": "/experiments"
         },
+        "models": {
+            "url": "/models"
+        },
         "identity": {
             "url": "/identity",
             "me": {
@@ -37,6 +40,12 @@
             },
             "scanStorage": {
                 "url": "/storage/scanStorage"
+            },
+            "allCustomModels": {
+                "url": "/storage/models/all"
+            },
+            "userModels": {
+                "url": "/storage/models/user"
             }
         }
     }