diff --git a/hbp_nrp_virtual_coach/pynrp/config.json b/hbp_nrp_virtual_coach/pynrp/config.json
index 74012ee63114df20080507515a330762b658b324..39bf74fad48761b62570d515bef4cf38360b0d4b 100644
--- a/hbp_nrp_virtual_coach/pynrp/config.json
+++ b/hbp_nrp_virtual_coach/pynrp/config.json
@@ -13,6 +13,9 @@
         "storage-authentication": "authentication/authenticate",
         "storage-experiment-list": "storage/experiments",
         "csv-files": "experiment/%s/csvfiles",
+        "storage-models": "storage/models/%s/%s",
+        "storage-models-delete": "storage/models/%s/%s",
+        "storage-models-import": "storage/models/%s/%s",
         "experiment-file": "storage/%s/%s",
         "save-data": "experiment"
     },
diff --git a/hbp_nrp_virtual_coach/pynrp/config.py b/hbp_nrp_virtual_coach/pynrp/config.py
index 0beab11ed9448974b10f3a8a07decf8c1f759d7b..3eb6654fff7f4862e77322efeff2bd72b2707d43 100644
--- a/hbp_nrp_virtual_coach/pynrp/config.py
+++ b/hbp_nrp_virtual_coach/pynrp/config.py
@@ -78,7 +78,9 @@ class Config(dict):
         self.__validate('proxy-services', ['experiment-list', 'available-servers', 'server-info',
                                            'experiment-clone', 'experiment-delete',
                                            'storage-authentication', 'storage-experiment-list',
-                                           'csv-files', 'experiment-file', 'experiment-import'])
+                                           'csv-files', 'experiment-file', 'experiment-import',
+                                           'storage-models', 'storage-models-delete',
+                                           'storage-models-import'])
         self.__validate('simulation-services', ['create', 'state', 'reset'])
         self.__validate('simulation-scripts', ['state-machine', 'transfer-function', 'brain',
                                                'sdf-world'])
diff --git a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
index e49621c07ed15b4a44c783264e9703df0b59dc68..74053713082a938143faca4b0fde261d293b6a3d 100644
--- a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
+++ b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
@@ -128,6 +128,27 @@ class TestVirtualCoach(unittest.TestCase):
                                       {'uuid': 'MockExperiment2_0', 'name': 'MockExperiment2_0'}]
 
 
+        self._mock_model_list = {'example_model_1':  {"name":"example_model_1",
+                                                      "displayName":"Example Model 1",
+                                                      "type":"robots",
+                                                      "isShared":"false",
+                                                      "isCustom":"false",
+                                                      "description":"Example Model 1 Description.",
+                                                      "thumbnail":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA",
+                                                      "path":"example_model_1",
+                                                      "sdf":"example_model_1.sdf",
+                                                      "configPath":"example_model_1/model.config"},
+                                 'example_model_2':  {"name":"example_model_2",
+                                                      "displayName":"Example Model 2",
+                                                      "type":"robots",
+                                                      "isShared":"true",
+                                                      "isCustom":"true",
+                                                      "description":"Example Model 2 Description.",
+                                                      "thumbnail":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA",
+                                                      "path":"example_model_2",
+                                                      "sdf":"example_model_2.sdf",
+                                                      "configPath":"example_model_2/model.config"}}
+
     def test_init_asserts_no_password(self):
         # invalid environment
         self.assertRaises(AssertionError, VirtualCoach, environment=True)
@@ -331,7 +352,6 @@ mock-server-5
     @patch('pynrp.virtual_coach.VirtualCoach._VirtualCoach__get_available_server_list')
     @patch('pynrp.virtual_coach.VirtualCoach._VirtualCoach__get_experiment_list')
     def test_launch_no_available_servers(self, mock_list, servers_list):
-
         # mock the Storage server call
         mock_list.return_value = self._mock_exp_list
         servers_list.return_value = []
@@ -600,7 +620,6 @@ mock-server-5
 
     @patch('requests.post')
     def test_set_experiment_list(self, request):
-
         class Request(object):
             status_code = 200
             content = None
@@ -623,3 +642,43 @@ mock-server-5
         path = os.path.join(self._tests_path, 'test_experiment_folder')
         response = self._vc.import_experiment(path)
         self.assertEqual(response.status_code, requests.codes.ok)
+
+
+    @patch('pynrp.virtual_coach.VirtualCoach._VirtualCoach__get_model_list')
+    @patch('sys.stdout', new_callable=StringIO)
+    def test_print_available_models(self, mock_stdout, mock_list):
+        # invalid dev option (should be True or False; it is set to False by default)
+        self.assertRaises(AssertionError, self._vc.print_available_models, 23)
+
+        # mock the server call
+        mock_list.return_value = self._mock_model_list
+        self._vc.print_available_models(model_type='robots', user='all')
+
+        model_table = """
++--------------+--------------+--------------+--------------+--------------+
+|     Name     | Display Name |   isShared   |   isCustom   | Description  |
++==============+==============+==============+==============+==============+
+| example_mode | Example      | false        | false        | Example      |
+| l_1          | Model 1      |              |              | Model 1      |
+|              |              |              |              | Description. |
++--------------+--------------+--------------+--------------+--------------+
+| example_mode | Example      | true         | true         | Example      |
+| l_2          | Model 2      |              |              | Model 2      |
+|              |              |              |              | Description. |
++--------------+--------------+--------------+--------------+--------------+
+        """
+        self.assertEqual(mock_stdout.getvalue().strip(), model_table.strip())
+
+    @patch('requests.post')
+    def test_import_model(self, mock_request):
+        mock_request.side_effect = [MagicMock(status_code=requests.codes.ok)]
+        self.assertRaises(Exception, self._vc.import_model, 'imaginary_file.zip')
+        path = os.path.join(self._tests_path, 'test.zip')
+        response = self._vc.import_model(path=path, model_type='robots')
+        self.assertEqual(response.status_code, requests.codes.ok)
+
+
+    @patch('pynrp.virtual_coach.VirtualCoach._VirtualCoach__get_model_list')
+    def test_delete_model(self, mock_list):
+        mock_list.return_value = self._mock_model_list
+        self.assertRaises(ValueError, self._vc.delete_model,  'foo_name', 'foo_type')
diff --git a/hbp_nrp_virtual_coach/pynrp/virtual_coach.py b/hbp_nrp_virtual_coach/pynrp/virtual_coach.py
index 7b8e38831e850d0419e6c361340105dd641bae63..99149021b6bf669f794e35ca9f94fc37168002f4 100644
--- a/hbp_nrp_virtual_coach/pynrp/virtual_coach.py
+++ b/hbp_nrp_virtual_coach/pynrp/virtual_coach.py
@@ -132,7 +132,6 @@ class VirtualCoach(object):
         # if the config is valid and the login doesn't fail, we're ready
         logger.info('Ready.')
 
-
     def __get_oidc_token(self, user_name, password):
         """
         Attempts to acquire a oidc server token based on the provided credentials
@@ -697,6 +696,110 @@ class VirtualCoach(object):
                             % (response.status_code, response))
         return response
 
+    def print_available_models(self, model_type, user="all"):
+        """
+        Prints available storage models.
+        :param model_type: type of the models to be shown ['environments', 'robots', 'brains']
+        :param user: username of models owner to be shown
+        """
+        assert isinstance(model_type, string_types)
+        assert isinstance(user, string_types)
+
+        if model_type not in ['robots', 'brains', 'environments']:
+            raise ValueError("Type must be a string in \
+                             ['robots', 'brains', 'environments']")
+
+        model_list = self.__get_model_list(model_type=model_type, user=user)
+
+        table = Texttable()
+        table.header(['Name', 'Display Name',
+                      'isShared', 'isCustom', 'Description'])
+        for name, v in sorted(iter(model_list.items()), key=lambda x: x[1]['name']):
+            table.add_row([name, v['displayName'], v['isShared'],
+                           v['isCustom'], v['description']])
+
+        # display the table
+        print(table.draw())
+
+    def import_model(self, path, model_type):
+        """
+        Imports a model (brain, robot or experiment as folder or zipped
+        folder) into user storage.
+        :param path: Path to the model folder or .zip file to be imported.
+        :param model_type: Model type to be imported ['environments', 'robots', 'brains']
+        """
+
+        assert isinstance(path, string_types)
+        assert isinstance(model_type, string_types)
+
+        if model_type not in ['robots', 'brains', 'environments']:
+            raise ValueError("Type must be a string in ['robots', \
+                             'brains', 'environments']")
+
+        if not os.path.isfile(path) and not os.path.isdir(path):
+            raise ValueError('The file or folder named %(path)s\
+                             does not exist.' % {'path': path})
+
+        if os.path.isdir(path):
+            # Handles a model folder
+            content = self.__get_directory_content(path)
+        else:
+            # Handles a zip file
+            try:
+                with open(path, 'rb') as f:
+                    content = f.read()
+            except Exception as e:
+                logger.error('The file %s could not be open', path)
+                raise e
+
+        file_headers = copy(self.__http_headers)
+        file_headers['Content-Type'] = 'application/octet-stream'
+        response = requests.post(self.__config['proxy-services']['storage-models-import'] %
+                                 (model_type, os.path.basename(path)),
+                                 data=content, headers=file_headers)
+
+        if response.status_code != requests.codes.ok:
+            raise Exception('Error when importing model: %d. Error: %s'
+                            % (response.status_code, response))
+
+        return response
+
+    def delete_model(self, model_type, name):
+        """
+        Deletes a model from user storage.
+        :param name: Name of the model to be deleted.
+        :param model_type: Model type to be deleted ['environments', 'robots', 'brains']
+        """
+        assert isinstance(model_type, string_types)
+        assert isinstance(name, string_types)
+
+        if model_type not in ['robots', 'brains', 'environments']:
+            raise ValueError("Type must be a string in ['robots',\
+                             'brains', 'environments']")
+
+        model_list = self.__get_model_list(model_type)
+        if name not in model_list:
+            raise ValueError('Model Name: "%s" is invalid, the model does not exist in your'
+                             ' storage. Please check the list of all models: \n%s'
+                             % (name, model_list.keys()))
+        self.__http_client.delete(self.__config['proxy-services']['storage-models-delete']
+                                  % (model_type, name,), body={})
+        logger.info('Model "%s" deleted successfully', name)
+
+    def __get_model_list(self, model_type, user="all"):
+        """
+        Internal helper to retrieve and parse the model list from the backend proxy.
+
+        :param model_type: type of the model ['environments', 'robots', 'brains']
+        :param user: username of model owner to be printed
+        """
+        response = requests.get(self.__config['proxy-services']['storage-models']
+                                % (user, model_type,), headers=self.__http_headers)
+
+        model_list = {model['name']: model for model in json.loads(response.content)}
+
+        return model_list
+
     @staticmethod
     def __zip_directory(dirpath, zip_filehandle):
         """
@@ -720,7 +823,7 @@ class VirtualCoach(object):
         Internal helper function
         It zips the target folder and returns its content
 
-        :param dirpath: path to the experiment folder to be zipped
+        :param dirpath: path to the experiment/model folder to be zipped
         """
         temp = tempfile.mktemp()
         zip_file = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
@@ -730,6 +833,7 @@ class VirtualCoach(object):
             logger.error('The folder %s could not be zipped', dirpath)
             raise e
         zip_file.close()
-        content = open(temp, 'rb').read()
+        with open(temp, 'rb') as f:
+            content = f.read()
         os.remove(temp)
         return content