diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
index 962df5138b5c00c40dec781fe7162713f5d8d2cb..54ea51d92095a82547eb541054ef2a81c53477aa 100644
--- a/bitbucket-pipelines.yml
+++ b/bitbucket-pipelines.yml
@@ -29,7 +29,7 @@ pipelines:
           - cp -af $HBP/user-scripts/config_files/platform_venv/* $VIRTUAL_ENV_PATH/lib/python2.7/site-packages/
 
           # Build
-          - export IGNORE_LINT='platform_venv|config_files'
+          - export IGNORE_LINT='platform_venv|config_files|examples/integration_test/test_experiment_folder'
           - make verify_base || { if [ -f pylint.txt ]; then echo "----------"; echo "PYLINT.TXT"; echo "----------";cat pylint.txt; fi; if [ -f pep8.txt ]; then echo "----------"; echo "PEP8.TXT"; echo "----------";cat pep8.txt; fi; exit 1; }
 
           # Coverage check
diff --git a/examples/integration_test/it.py b/examples/integration_test/it.py
index b544d7c0b6f6869b56cfa1165fad08cd5d6ebf3e..acdac7be2cbec26ac6c00da4694208f3843de835 100644
--- a/examples/integration_test/it.py
+++ b/examples/integration_test/it.py
@@ -39,12 +39,12 @@ try:
 
 except ImportError:
     print
-    print 'Failed to import the VirtualCoach or access packages in the platform_venv! Aborting.'
+    print('Failed to import the VirtualCoach or access packages in the platform_venv! Aborting.')
     print
-    print 'Please make sure you have:'
+    print('Please make sure you have:')
     print
-    print '\t1. run "make devinstall" in the VirtualCoach repo'
-    print '\t2. use "cle-virtual-coach it.py" to run this script'
+    print('\t1. run "make devinstall" in the VirtualCoach repo')
+    print('\t2. use "cle-virtual-coach it.py" to run this script')
     print
     sys.exit(-1)
 
@@ -110,7 +110,7 @@ def run(oidc_username, storage_username):
 
     # running array of test case results, unfortunately we have to hardcode the number of
     # test cases because the indeterminate progress bar is not helpful for the tester
-    NUM_TEST_CASES = 28
+    NUM_TEST_CASES = 30
     results = TestCases(NUM_TEST_CASES)
 
     try:
@@ -174,6 +174,32 @@ def run(oidc_username, storage_username):
             raise TestCaseError('Deleting a cloned Experiment failed')
         results.done(True)
 
+        ##
+        ## Import an Experiment Folder
+        ##
+
+        results.start('Importing an experiment folder')
+        response = vc.import_experiment('test_experiment_folder')
+        new_experiment_id = json.loads(response.text)['destFolderName']
+        if new_experiment_id not in vc._VirtualCoach__get_experiment_list(cloned=True):
+            raise TestCaseError('Importing an experiment folder failed')
+        else:
+            vc.delete_cloned_experiment(new_experiment_id)
+        results.done(True)
+
+        ##
+        ## Import an Experiment Zipped Folder
+        ##
+
+        results.start('Importing a zipped experiment folder')
+        response = vc.import_experiment('test_experiment_folder.zip')
+        new_experiment_id = json.loads(response.text)['destFolderName']
+        if new_experiment_id not in vc._VirtualCoach__get_experiment_list(cloned=True):
+            raise TestCaseError('Importing an experiment folder failed')
+        else:
+            vc.delete_cloned_experiment(new_experiment_id)
+        results.done(True)
+
         ##
         ## Experiment Launch and Simulation State Interaction
         ##
diff --git a/examples/integration_test/test_experiment_folder.zip b/examples/integration_test/test_experiment_folder.zip
new file mode 100644
index 0000000000000000000000000000000000000000..ffb8e92506759a282c0ad578cc09427da0d7c6ec
Binary files /dev/null and b/examples/integration_test/test_experiment_folder.zip differ
diff --git a/examples/integration_test/test_experiment_folder/ExDTemplateHusky.exc b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.exc
new file mode 100644
index 0000000000000000000000000000000000000000..3701f06494af9f50c303cd7f71dd9d5b4149d7ef
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.exc
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ExD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+     xmlns="http://schemas.humanbrainproject.eu/SP10/2014/ExDConfig"
+     xsi:schemaLocation="http://schemas.humanbrainproject.eu/SP10/2014/ExDConfig ../ExDConfFile.xsd">
+    <name>Template Husky in empty environment</name>
+    <thumbnail>ExDTemplateHusky.jpg</thumbnail>
+    <description>This experiment loads the Husky robot in an empty world, with an idle brain and basic transfer functions. You are free to edit it.</description>
+    <tags>template husky robotics empty</tags>
+    <timeout>840</timeout>
+    <configuration type="3d-settings" src="ExDTemplateHusky.ini"/>
+    <configuration type="brainvisualizer" src="brainvisualizer.json"/>
+	<configuration type="user-interaction-settings" src="ExDTemplateHusky.uis"/>
+    <maturity>production</maturity>
+    <environmentModel src="empty_world/empty_world.sdf">
+        <robotPose robotId="husky" x="0.0" y="0.0" z="0.5" roll="0.0" pitch="-0.0" yaw="0.0"/>
+    </environmentModel>
+    <bibiConf src="template_husky.bibi"/>
+    <cameraPose>
+        <cameraPosition x="4.5" y="0" z="1.8"/>
+        <cameraLookAt x="0" y="0" z="0.6"/>
+    </cameraPose>
+</ExD>
diff --git a/examples/integration_test/test_experiment_folder/ExDTemplateHusky.ini b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.ini
new file mode 100644
index 0000000000000000000000000000000000000000..0091df91f8d2f1d66bc2c12b8e4147a84626564a
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.ini
@@ -0,0 +1 @@
+{"dynamicEnvMap":true,"shadows":true,"antiAliasing":true,"ssao":false,"ssaoDisplay":false,"ssaoClamp":"0.94","ssaoLumInfluence":"1","rgbCurve":{"red":[[0,0],[0.410400390625,0.4481201171875],[0.761962890625,0.7957763671875],[1,1]],"green":[[0,0],[0.261962890625,0.2801513671875],[0.718994140625,0.7371826171875],[1,1]],"blue":[[0,0],[0.265869140625,0.2606201171875],[0.840087890625,0.7645263671875],[1,1]]},"levelsInBlack":"0.09","levelsInGamma":0.52,"levelsInWhite":"0.8","levelsOutBlack":0,"levelsOutWhite":1,"skyBox":"softgradient","sun":"","bloom":false,"bloomStrength":"0.83","bloomRadius":"0.29","bloomThreshold":"0.91","fog":true,"fogDensity":"0.02","fogColor":"#97a2af","pbrMaterial":true,"shadowSettings":[{"lightName":"sun","mapSize":4096,"cameraBottom":-10,"cameraLeft":-10,"cameraRight":10,"cameraTop":25,"bias":0.0003,"radius":1.2}]}
\ No newline at end of file
diff --git a/examples/integration_test/test_experiment_folder/ExDTemplateHusky.jpg b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9838a4aa4f1e13bd6abcbcdf9eb72de33979104a
Binary files /dev/null and b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.jpg differ
diff --git a/examples/integration_test/test_experiment_folder/ExDTemplateHusky.uis b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.uis
new file mode 100644
index 0000000000000000000000000000000000000000..cca085f4eef1f9fe3b28de95fe4feebb21adad18
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/ExDTemplateHusky.uis
@@ -0,0 +1,9 @@
+{
+    "camera": {
+        "defaultMode": "free-camera",
+        "sensitivity": {
+            "translation": 1.0,
+            "rotation": 1.0
+        }
+    }
+}
diff --git a/examples/integration_test/test_experiment_folder/all_neurons_spike_monitor.py b/examples/integration_test/test_experiment_folder/all_neurons_spike_monitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..34a270b2a084c06fa66316ac45b90ceb124dfd3e
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/all_neurons_spike_monitor.py
@@ -0,0 +1,12 @@
+# Imported Python Transfer Function
+#
+import hbp_nrp_cle.tf_framework as nrp
+# This specifies that the neurons 0 to 2 of the circuit population
+# should be monitored. You can see them in the spike train widget
+@nrp.NeuronMonitor(nrp.brain.record, nrp.spike_recorder)
+def all_neurons_spike_monitor(t):
+    # Uncomment to log into the 'log-console' visible in the simulation
+    # clientLogger.info("Time: ", t)
+    return True
+#
+
diff --git a/examples/integration_test/test_experiment_folder/brainvisualizer.json b/examples/integration_test/test_experiment_folder/brainvisualizer.json
new file mode 100644
index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/brainvisualizer.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/examples/integration_test/test_experiment_folder/grab_image.py b/examples/integration_test/test_experiment_folder/grab_image.py
new file mode 100644
index 0000000000000000000000000000000000000000..3209ff7a7a6ee2452668e006f1ee407f510eb348
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/grab_image.py
@@ -0,0 +1,12 @@
+# Imported Python Transfer Function
+#
+import sensor_msgs.msg
+@nrp.MapRobotSubscriber("camera", Topic('/husky/husky/camera', sensor_msgs.msg.Image))
+@nrp.MapSpikeSource("input_neuron", nrp.brain.neurons[0], nrp.poisson)
+@nrp.Robot2Neuron()
+# Example TF: get image and fire at constant rate. You could do something with the image here and fire accordingly.
+def grab_image(t, camera, input_neuron):
+    image = camera.value
+    input_neuron.rate = 10
+#
+
diff --git a/examples/integration_test/test_experiment_folder/template_husky.bibi b/examples/integration_test/test_experiment_folder/template_husky.bibi
new file mode 100644
index 0000000000000000000000000000000000000000..a7452cabdbd23918cca265c6cf29008930fbe4bc
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/template_husky.bibi
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<ns1:bibi xmlns:ns1="http://schemas.humanbrainproject.eu/SP10/2014/BIBI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<ns1:brainModel>
+		<ns1:file>brain_model/idle_brain.py</ns1:file>
+		<ns1:populations from="0" population="neurons" to="2" xsi:type="ns1:Range"/>
+		<ns1:populations from="0" population="record" to="2" xsi:type="ns1:Range"/>
+	</ns1:brainModel>
+	<ns1:bodyModel robotId="husky">husky_model/model.sdf</ns1:bodyModel>
+	<ns1:transferFunction src="all_neurons_spike_monitor.py" xsi:type="ns1:PythonTransferFunction"/>
+	<ns1:transferFunction src="turn_around.py" xsi:type="ns1:PythonTransferFunction"/>
+	<ns1:transferFunction src="grab_image.py" xsi:type="ns1:PythonTransferFunction"/>
+</ns1:bibi>
diff --git a/examples/integration_test/test_experiment_folder/turn_around.py b/examples/integration_test/test_experiment_folder/turn_around.py
new file mode 100644
index 0000000000000000000000000000000000000000..ba5da46d3e80a194502245fc26b1df7b310b657f
--- /dev/null
+++ b/examples/integration_test/test_experiment_folder/turn_around.py
@@ -0,0 +1,14 @@
+# Imported Python Transfer Function
+#
+import hbp_nrp_cle.tf_framework as nrp
+from hbp_nrp_cle.robotsim.RobotInterface import Topic
+import geometry_msgs.msg
+@nrp.MapSpikeSink("output_neuron", nrp.brain.neurons[1], nrp.leaky_integrator_alpha)
+@nrp.Neuron2Robot(Topic('/husky/husky/cmd_vel', geometry_msgs.msg.Twist))
+# Example TF: get output neuron voltage and output constant on robot actuator. You could do something with the voltage here and command the robot accordingly.
+def turn_around(t, output_neuron):
+    voltage=output_neuron.voltage
+    return geometry_msgs.msg.Twist(linear=geometry_msgs.msg.Vector3(0,0,0),
+                                   angular=geometry_msgs.msg.Vector3(0,0,5))
+#
+
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
index 53d842591cec9a932bdeba47510fc5c42e10fe65..118d9a7ed40e9438494a80e640f244f5a9726189 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
@@ -86,7 +86,7 @@ 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'])
+                                           'csv-files', 'experiment-file', 'experiment-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/hbp_nrp_virtual_coach/tests/test.zip b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test.zip
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_experiment_folder/empty_file.txt b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_experiment_folder/empty_file.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
index 77741b6b5ea70c0dc6020b07672f50b2ae9a66dd..5a98e3a1f98fa89440af298d1245c78cd12c2c6e 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
@@ -39,6 +39,7 @@ import copy
 from dateutil import parser
 import json
 from StringIO import StringIO
+import os
 
 
 class TestVirtualCoach(unittest.TestCase):
@@ -58,6 +59,7 @@ class TestVirtualCoach(unittest.TestCase):
             return realimport(name, globals, locals, fromlist, level)
         builtins.__import__ = rospy_import_fail
 
+        self._tests_path = os.path.dirname(os.path.realpath(__file__))
 
         self._vc = VirtualCoach(environment='local', storage_username='nrpuser')
 
@@ -626,3 +628,18 @@ mock-server-5
         request.return_value = Request()
         self._vc.set_experiment_file('exp_id', 'file_name', 'file_content')
 
+    @patch('requests.post')
+    def test_import_experiment_zip(self, mock_request):
+        mock_request.side_effect = [MagicMock(status_code=requests.codes.ok)]
+        self.assertRaises(Exception, self._vc.import_experiment, 'imaginary_file.zip')
+        path = os.path.join(self._tests_path, 'test.zip')
+        response = self._vc.import_experiment(path)
+        self.assertEqual(response.status_code, requests.codes.ok)
+
+    @patch('requests.post')
+    def test_import_experiment_folder(self, mock_request):
+        mock_request.side_effect = [MagicMock(status_code=requests.codes.ok)]
+        self.assertRaises(Exception, self._vc.import_experiment, 'imaginary_experiment_folder')
+        path = os.path.join(self._tests_path, 'test_experiment_folder')
+        response = self._vc.import_experiment(path)
+        self.assertEqual(response.status_code, requests.codes.ok)
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
index 3edcd0286d732a8f4b7f689cd5ab89a16e6e8a5e..b5a5e896e258fd9485c85fc58409ae2c20644823 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
@@ -38,6 +38,9 @@ import requests
 import logging
 import getpass
 import httplib
+import os
+import zipfile
+import tempfile
 from texttable import Texttable
 from copy import copy
 
@@ -603,3 +606,74 @@ class VirtualCoach(object):
                             % (response.status_code, response))
 
         return response.content
+
+    @staticmethod
+    def __zip_directory(dirpath, zip_filehandle):
+        """
+        Internal helper function
+        It zips the target directory
+
+        :param dirpath: Path to the experiment folder to be zipped
+        :param zip_filehandle: Handle of the zip file to be populated
+        """
+        dirpath = os.path.abspath(dirpath)
+        dirname = os.path.dirname(dirpath)
+        basename = os.path.basename(dirpath)
+        os.chdir(dirname)
+        for root, _, files in os.walk(basename):
+            for f in files:
+                zip_filehandle.write(os.path.join(root, f))
+
+    @staticmethod
+    def __get_directory_content(dirpath):
+        """
+        Internal helper function
+        It zips the target folder and returns its content
+
+        :param dirpath: path to the experiment folder to be zipped
+        """
+        temp = tempfile.mktemp()
+        zip_file = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
+        try:
+            VirtualCoach.__zip_directory(dirpath, zip_file)
+        except Exception as e:
+            logger.error('The folder %s could not be zipped', dirpath)
+            raise e
+        zip_file.close()
+        content = open(temp, 'r').read()
+        os.remove(temp)
+        return content
+
+    def import_experiment(self, path):
+        """
+        Imports an experiment folder, possibly a zipped folder, into user storage
+
+        :param path: path to the experiment folder or to the zip file to be imported
+        :type path: str
+        """
+        if not isinstance(path, str):
+            raise TypeError('The provided argument is not a string.')
+        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})
+
+        url = self.__config['proxy-services']['experiment-import']
+        file_headers = copy(self.__http_headers)
+        file_headers['Content-Type'] = 'application/octet-stream'
+        content = None
+        if os.path.isdir(path):
+            # Handles an experiment folder
+            content = VirtualCoach.__get_directory_content(path)
+        else:
+            # Handles a zip file
+            try:
+                content = open(path, 'r').read()
+            except Exception as e:
+                logger.error('The file %s could not be open', path)
+                raise e
+
+        response = requests.post(url, data=content, headers=file_headers)
+        # pylint: disable=no-member
+        if response.status_code != requests.codes.ok:
+            raise Exception('Error when importing experiment: %d. Error: %s'
+                            % (response.status_code, response))
+        return response
diff --git a/verify.sh b/verify.sh
index 58373b3b02445a7c37986dbc7e8adf0b119468f2..d09f05fbf3a3dffbca2d1575d60a765cb56be088 100755
--- a/verify.sh
+++ b/verify.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 # This script is designed for local usage.
 
-# Ignore generated files.
-export IGNORE_LINT="platform_venv"
+# Ignore generated files and files provided for testing.
+export IGNORE_LINT="platform_venv|config_files|examples/integration_test/test_experiment_folder"
 export VIRTUAL_ENV=$NRP_VIRTUAL_ENV
 
 # This script only runs static code analysis, the tests can be run separately using run_tests.sh