diff --git a/.gitlab-ci.yaml b/.gitlab-ci.yaml
index 3184bf82418b97b2a98082787df277f5f11648a8..018915d2c8dd6837603c76ab71f62d88118b6b35 100644
--- a/.gitlab-ci.yaml
+++ b/.gitlab-ci.yaml
@@ -8,6 +8,7 @@ stages:
 - deploy
 - e2e-test
 - post-deploy
+- reports
 
 variables:
   GIT_DEPTH: 1
@@ -274,3 +275,135 @@ tags_api_smoketest_negative:
     reports:
       junit: api_smoketest_tags_api_negative.xml
 
+.hivemind_reports: &common_api_reports
+  stage: reports
+  environment: hive-4.pl.syncad.com
+  needs:
+    - job: hivemind_stop_server
+      artifacts: true
+
+  variables:
+    GIT_STRATEGY: none
+
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+      when: always
+    - if: '$CI_PIPELINE_SOURCE == "push"'
+      when: manual
+    - when: on_success
+
+  tags:
+      - hivemind
+
+  allow_failure: true
+
+bridge_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/bridge_api_patterns/ api_smoketest_bridge.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_bridge.html
+
+bridge_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/bridge_api_negative/ api_smoketest_bridge_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_bridge_negative.html
+
+condenser_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/condenser_api_patterns/ api_smoketest_condenser_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_condenser_api.html
+
+condenser_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/condenser_api_negative/ api_smoketest_condenser_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_condenser_api_negative.html
+
+database_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/database_api_patterns/ api_smoketest_database_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_database_api.html
+
+database_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/database_api_negative/ api_smoketest_database_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_database_api_negative.html
+
+follow_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/follow_api_patterns/ api_smoketest_follow_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_follow_api.html
+
+follow_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/follow_api_negative/ api_smoketest_follow_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_follow_api_negative.html
+
+tags_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/tags_api_patterns/ api_smoketest_tags_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_tags_api.html
+
+tags_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py tests/tests_api/hivemind/tavern/tags_api_negative/ api_smoketest_tags_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_tags_api_negative.html
+
diff --git a/scripts/xml_report_parser.py b/scripts/xml_report_parser.py
new file mode 100755
index 0000000000000000000000000000000000000000..29a6bbe4580622e6579593bebfb0a9126da38e05
--- /dev/null
+++ b/scripts/xml_report_parser.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+
+import xml.dom.minidom
+import os
+from sys import exit
+from json import dumps
+
+def get_request_from_yaml(path_to_yaml):
+    import yaml
+    yaml_document = None
+    with open(path_to_yaml, "r") as yaml_file:
+        yaml_document = yaml.load(yaml_file, Loader=yaml.BaseLoader)
+    if "stages" in yaml_document:
+        if "request" in yaml_document["stages"][0]:
+            json_parameters = yaml_document["stages"][0]["request"].get("json", None)
+            assert json_parameters is not None, "Unable to find json parameters in request"
+            return dumps(json_parameters)
+    return ""
+
+def make_class_path_dict(root_dir):
+    import os
+    from fnmatch import fnmatch
+
+    pattern = "*.tavern.yaml"
+
+    ret = {}
+
+    for path, subdirs, files in os.walk(root_dir):
+        for name in files:
+            if fnmatch(name, pattern):
+                test_path = os.path.join(path, name)
+                ret[test_path.replace("/", ".")] = test_path
+    return ret
+
+def class_to_path(class_name, class_to_path_dic):
+    from fnmatch import fnmatch
+    for c, p in class_to_path_dic.items():
+        if fnmatch(c, "*" + class_name):
+            return p
+    return None
+
+if __name__ == '__main__':
+    above_treshold = False
+    import argparse
+    parser = argparse.ArgumentParser()
+    parser.add_argument("path_to_test_dir", type = str, help = "Path to test directory for given xml file")
+    parser.add_argument("xml_file", type = str, help = "Path to report file in xml format")
+    parser.add_argument("--time-threshold", dest="time_threshold", type=float, default=1.0, help="Time threshold for test execution time, tests with execution time greater than threshold will be marked on red.")
+    args = parser.parse_args()
+    html_file, _ = os.path.splitext(args.xml_file)
+    html_file += ".html"
+    class_to_path_dic = make_class_path_dict(args.path_to_test_dir)
+    with open(html_file, "w") as ofile:
+        ofile.write("<html>\n")
+        ofile.write("  <head>\n")
+        ofile.write("    <style>\n")
+        ofile.write("      table, th, td {\n")
+        ofile.write("        border: 1px solid black;\n")
+        ofile.write("        border-collapse: collapse;\n")
+        ofile.write("      }\n")
+        ofile.write("      th, td {\n")
+        ofile.write("        padding: 15px;\n")
+        ofile.write("      }\n")
+        ofile.write("    </style>\n")
+        ofile.write("  </head>\n")
+        ofile.write("  <body>\n")
+        ofile.write("    <table>\n")
+        ofile.write("      <tr><th>Test name</th><th>Time [s]</th></tr>\n")
+        document = xml.dom.minidom.parse(args.xml_file)
+        tests_collection = document.documentElement
+        for test in tests_collection.getElementsByTagName("testcase"):
+            if test.hasAttribute("name") and test.hasAttribute("time"):
+                if float(test.getAttribute("time")) > args.time_threshold:
+                    ofile.write("      <tr><td>{}<br/>Parameters: {}</td><td bgcolor=\"red\">{}</td></tr>\n".format(test.getAttribute("name"), get_request_from_yaml(class_to_path(test.getAttribute("classname"), class_to_path_dic)), test.getAttribute("time")))
+                    above_treshold = True
+                else:
+                    ofile.write("      <tr><td>{}</td><td>{}</td></tr>\n".format(test.getAttribute("name"), test.getAttribute("time")))
+        ofile.write("    </table>\n")
+        ofile.write("  </body>\n")
+        ofile.write("</html>\n")
+    if above_treshold:
+        exit(1)
+    exit(0)