diff --git a/src/api-server/model/QosData.cpp b/src/api-server/model/QosData.cpp
index 8261a6c34c9d829fbc648ce56c26d596f499de80..ced3f5604f0f878b3030ea55b7529dbe1ebd5a0b 100644
--- a/src/api-server/model/QosData.cpp
+++ b/src/api-server/model/QosData.cpp
@@ -62,8 +62,7 @@ QosData::QosData() {
 void QosData::validate() const {
   std::stringstream msg;
   if (!validate(msg)) {
-    //        throw
-    //        org::openapitools::server::helpers::ValidationException(msg.str());
+    throw oai::model::common::helpers::ValidationException(msg.str());
   }
 }
 
@@ -89,27 +88,35 @@ bool QosData::validate(
       msg << currentValuePath << ": must be less than or equal to 255;";
     }
   }
-  /*
+
   if (maxbrUlIsSet()) {
     const std::string& value           = m_MaxbrUl;
     const std::string currentValuePath = _pathPrefix + ".maxbrUl";
+    success &= helpers::validate_regex(
+        BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath);
   }
 
   if (maxbrDlIsSet()) {
     const std::string& value           = m_MaxbrDl;
     const std::string currentValuePath = _pathPrefix + ".maxbrDl";
+    success &= helpers::validate_regex(
+        BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath);
   }
 
   if (gbrUlIsSet()) {
     const std::string& value           = m_GbrUl;
     const std::string currentValuePath = _pathPrefix + ".gbrUl";
+    success &= helpers::validate_regex(
+        BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath);
   }
 
   if (gbrDlIsSet()) {
     const std::string& value           = m_GbrDl;
     const std::string currentValuePath = _pathPrefix + ".gbrDl";
+    success &= helpers::validate_regex(
+        BANDWIDTH_VALIDATION_REGEX, value, msg, currentValuePath);
   }
-  */
+
   if (priorityLevelIsSet()) {
     const int32_t& value               = m_PriorityLevel;
     const std::string currentValuePath = _pathPrefix + ".priorityLevel";
@@ -299,7 +306,9 @@ void to_json(nlohmann::json& j, const QosData& o) {
 }
 
 void from_json(const nlohmann::json& j, QosData& o) {
-  j.at("qosId").get_to(o.m_QosId);
+  if (j.find("qosId") != j.end()) {
+    j.at("qosId").get_to(o.m_QosId);
+  }
   if (j.find("5qi") != j.end()) {
     j.at("5qi").get_to(o.m_r_5qi);
     o.m_r_5qiIsSet = true;
diff --git a/src/api-server/model/QosData.h b/src/api-server/model/QosData.h
index b14369a4fd2290fc48aae25892c9905707bd1c07..81e9409e00101284754cb40e31f8d52b398e14b1 100644
--- a/src/api-server/model/QosData.h
+++ b/src/api-server/model/QosData.h
@@ -31,6 +31,9 @@ namespace model {
 ///
 /// </summary>
 class QosData {
+  const std::string BANDWIDTH_VALIDATION_REGEX =
+      R"(^\d+(\.\d+)? (bps|Kbps|Mbps|Gbps|Tbps)$)";
+
  public:
   QosData();
   virtual ~QosData() = default;
diff --git a/src/oai-cn5g-common-src b/src/oai-cn5g-common-src
index 42e49ae9764b228fe571d8a331f560f2f1c5729f..3e64f08aac80e71337c78f0af73a37f722d98309 160000
--- a/src/oai-cn5g-common-src
+++ b/src/oai-cn5g-common-src
@@ -1 +1 @@
-Subproject commit 42e49ae9764b228fe571d8a331f560f2f1c5729f
+Subproject commit 3e64f08aac80e71337c78f0af73a37f722d98309
diff --git a/src/oai_pcf/CMakeLists.txt b/src/oai_pcf/CMakeLists.txt
index a6921e38fa69aaaa100f5393f7a6d0f5c0224e43..70d90bd0dde109e6cda2cf4f04afd74767fc563d 100644
--- a/src/oai_pcf/CMakeLists.txt
+++ b/src/oai_pcf/CMakeLists.txt
@@ -293,6 +293,7 @@ add_executable(pcf
 include(${BUILD_TOP_DIR}/pcf/used_common_files.cmake)
 include(${SRC_TOP_DIR}/${MOUNTED_COMMON}/logger/logger.cmake)
 include(${SRC_TOP_DIR}/${MOUNTED_COMMON}/config/config.cmake)
+include_directories(${SRC_TOP_DIR}/${MOUNTED_COMMON}/utils)
 
 IF(STATIC_LINKING)
     SET(CMAKE_EXE_LINKER_FLAGS "-static")
diff --git a/src/pcf_app/pcf_config.cpp b/src/pcf_app/pcf_config.cpp
index 02e78b7c64080f78a802e5100d9a8110d85bf26a..642d5818917b2ec1ce6325be7c3bf1eadf560921 100644
--- a/src/pcf_app/pcf_config.cpp
+++ b/src/pcf_app/pcf_config.cpp
@@ -45,7 +45,7 @@ oai::config::pcf::pcf_config::pcf_config(
       sbi_interface("SBI", "oai-pcf", 80, "v1", "eth0"),
       policy_config(
           DEFAULT_POLICY_DECISIONS_PATH, DEFAULT_PCC_RULES_PATH,
-          DEFAULT_TRAFFIC_RULES_PATH));
+          DEFAULT_TRAFFIC_RULES_PATH, DEFAULT_QOS_DATA_PATH));
 
   auto nrf = std::make_shared<nf>(
       NRF_CONFIG_NAME, "oai-nrf",
diff --git a/src/pcf_app/pcf_config.hpp b/src/pcf_app/pcf_config.hpp
index 95a2db9b878f16cb4fec64e2daba2ad4836ad817..74e57e1dadea14a2de390c584007fc40915ea7f8 100644
--- a/src/pcf_app/pcf_config.hpp
+++ b/src/pcf_app/pcf_config.hpp
@@ -39,6 +39,7 @@ const std::string DEFAULT_TRAFFIC_RULES_PATH =
     "/openair-pcf/policies/traffic_rules";
 const std::string DEFAULT_POLICY_DECISIONS_PATH =
     "/openair-pcf/policies/policy_decisions";
+const std::string DEFAULT_QOS_DATA_PATH = "/openair-pcf/policies/qos_data";
 
 class pcf_config : public oai::config::config {
  public:
diff --git a/src/pcf_app/pcf_config_types.cpp b/src/pcf_app/pcf_config_types.cpp
index 9c0cf59108010d156a30a2894a0e80a5c85d2123..6331b2086ad61c4b1df0d1c5a144aa67023220b1 100644
--- a/src/pcf_app/pcf_config_types.cpp
+++ b/src/pcf_app/pcf_config_types.cpp
@@ -37,14 +37,15 @@ using namespace oai::config::pcf;
 
 policy_config::policy_config(
     const std::string& policy_decisions_path, const std::string& pcc_rules_path,
-    const std::string& traffic_rules_path) {
+    const std::string& traffic_rules_path, const std::string& qos_data_path) {
   m_config_name = "Policy";
   m_traffic_rules_path =
       string_config_value("Traffic Rules", traffic_rules_path);
   m_pcc_rules_path = string_config_value("PCC Rules", pcc_rules_path);
   m_policy_decisions_path =
       string_config_value("Policy Decisions", policy_decisions_path);
-  m_set = true;
+  m_qos_data_path = string_config_value("QoS Data", qos_data_path);
+  m_set           = true;
 }
 
 void policy_config::from_yaml(const YAML::Node& node) {
@@ -57,23 +58,31 @@ void policy_config::from_yaml(const YAML::Node& node) {
   if (node["traffic_rules_path"]) {
     m_traffic_rules_path.from_yaml(node["traffic_rules_path"]);
   }
+  if (node["qos_data_path"]) {
+    m_qos_data_path.from_yaml(node["qos_data_path"]);
+  }
 }
 
 std::string policy_config::to_string(const std::string& indent) const {
   if (!m_set) return "";
   std::string out;
-  unsigned int inner_width = get_inner_width(indent.length());
-  out.append(m_config_name).append("\n");
+  std::string title_fmt = get_title_formatter(0);
+  std::string value_fmt = get_value_formatter(1);
+
+  out.append(indent).append(fmt::format(title_fmt, m_config_name));
   out.append(indent).append(fmt::format(
-      BASE_FORMATTER, OUTER_LIST_ELEM,
-      m_policy_decisions_path.get_config_name(), inner_width,
+      value_fmt, m_policy_decisions_path.get_config_name(),
       m_policy_decisions_path.get_value()));
   out.append(indent).append(fmt::format(
-      BASE_FORMATTER, OUTER_LIST_ELEM, m_pcc_rules_path.get_config_name(),
-      inner_width, m_pcc_rules_path.get_value()));
+      value_fmt, m_pcc_rules_path.get_config_name(),
+      m_pcc_rules_path.get_value()));
+  out.append(indent).append(fmt::format(
+      value_fmt, m_traffic_rules_path.get_config_name(),
+      m_traffic_rules_path.get_value()));
   out.append(indent).append(fmt::format(
-      BASE_FORMATTER, OUTER_LIST_ELEM, m_traffic_rules_path.get_config_name(),
-      inner_width, m_traffic_rules_path.get_value()));
+      value_fmt, m_qos_data_path.get_config_name(),
+      m_qos_data_path.get_value()));
+
   return out;
 }
 
@@ -89,6 +98,10 @@ const std::string& policy_config::get_traffic_rules_path() const {
   return m_traffic_rules_path.get_value();
 }
 
+const std::string& policy_config::get_qos_data_path() const {
+  return m_qos_data_path.get_value();
+}
+
 pcf_config_type::pcf_config_type(
     const std::string& name, const std::string& host, const sbi_interface& sbi,
     const policy_config& policy)
diff --git a/src/pcf_app/pcf_config_types.hpp b/src/pcf_app/pcf_config_types.hpp
index fd8b8378b6c7a900903d78c076a30afa089c93d8..f6c28b33f1eec4ea6aac552c3d89991ef0217469 100644
--- a/src/pcf_app/pcf_config_types.hpp
+++ b/src/pcf_app/pcf_config_types.hpp
@@ -37,11 +37,13 @@ class policy_config : public config_type {
   string_config_value m_pcc_rules_path;
   string_config_value m_policy_decisions_path;
   string_config_value m_traffic_rules_path;
+  string_config_value m_qos_data_path;
 
  public:
   explicit policy_config(
       const std::string& policy_decisions_path,
-      const std::string& pcc_rules_path, const std::string& traffic_rules_path);
+      const std::string& pcc_rules_path, const std::string& traffic_rules_path,
+      const std::string& qos_data_path);
 
   void from_yaml(const YAML::Node& node) override;
 
@@ -49,6 +51,7 @@ class policy_config : public config_type {
   [[nodiscard]] const std::string& get_pcc_rules_path() const;
   [[nodiscard]] const std::string& get_policy_decisions_path() const;
   [[nodiscard]] const std::string& get_traffic_rules_path() const;
+  [[nodiscard]] const std::string& get_qos_data_path() const;
 };
 
 class pcf_config_type : public nf {
diff --git a/src/pcf_app/sm_policy/policy_provisioning_file.cpp b/src/pcf_app/sm_policy/policy_provisioning_file.cpp
index e1b1869e789d8e06cd27187484e71c39f8ed51e5..ed80bfa85c38abddac3f192e303ef395e44d2f42 100644
--- a/src/pcf_app/sm_policy/policy_provisioning_file.cpp
+++ b/src/pcf_app/sm_policy/policy_provisioning_file.cpp
@@ -37,6 +37,7 @@
 #include "pcf_config.hpp"
 #include "logger.hpp"
 #include "Snssai.h"
+#include "conv.hpp"
 
 using namespace oai::pcf::app;
 using namespace oai::config::pcf;
@@ -52,6 +53,7 @@ bool policy_provisioning_file::read_all_policy_files() {
   std::vector<YAML::Node> traffic_controls;
   std::vector<YAML::Node> pcc_rules;
   std::vector<YAML::Node> policy_decisions;
+  std::vector<YAML::Node> qos_data;
 
   if (!read_all_files_in_dir(
           pcf_cfg->get_pcf_policy().get_traffic_rules_path(),
@@ -70,50 +72,55 @@ bool policy_provisioning_file::read_all_policy_files() {
         "Could not load mandatory policy decisions configuration");
     return false;
   }
+  if (!read_all_files_in_dir(
+          pcf_cfg->get_pcf_policy().get_qos_data_path(), qos_data)) {
+    Logger::pcf_app().warn("Could not load QoS Data files");
+  }
 
   auto traffic_control_objects =
       convert_yaml_to_model<TrafficControlData>(traffic_controls);
   auto pcc_rule_objects = convert_yaml_to_model<PccRule>(pcc_rules);
+  auto qos_data_objects = convert_yaml_to_model<QosData>(qos_data);
 
   if (traffic_control_objects.empty()) {
     Logger::pcf_app().warn("No traffic control descriptions are loaded");
   }
+  if (qos_data_objects.empty()) {
+    Logger::pcf_app().warn("No QoS data descriptions are loaded");
+  }
   if (pcc_rule_objects.empty()) {
     Logger::pcf_app().warn("No mandatory PCC rules are loaded");
     return false;
   }
 
   // we need to manually adjust the IDs of the PCC rules and traffic controls
-  for (auto traffic : traffic_control_objects) {
+  for (auto& traffic : traffic_control_objects) {
     traffic.second.setTcId(traffic.first);
-    traffic_control_objects[traffic.first] = traffic.second;
   }
-  for (auto pcc : pcc_rule_objects) {
+  for (auto& qos : qos_data_objects) {
+    qos.second.setQosId(qos.first);
+  }
+  for (auto& pcc : pcc_rule_objects) {
     pcc.second.setPccRuleId(pcc.first);
 
-    // check if traffic control data exists
-    auto reftc      = pcc.second.getRefTcData();
-    auto traffic_it = reftc.begin();
-    while (traffic_it != reftc.end()) {
-      auto traffic_exists = traffic_control_objects.find(*traffic_it);
-      if (traffic_exists == traffic_control_objects.end()) {
-        Logger::pcf_app().warn(
-            "You have referenced Traffic Control ID %s in PCC Rule %s, but it "
-            "does not exist. It is removed from this PCC rule",
-            (*traffic_it).c_str(), pcc.first.c_str());
-        traffic_it = reftc.erase(traffic_it);
-      } else {
-        ++traffic_it;
-      }
-    }
+    auto reftc = pcc.second.getRefTcData();
+    remove_ids_not_in_map<TrafficControlData>(
+        traffic_control_objects, reftc, "Traffic Rules", pcc.first);
+
+    auto refqos = pcc.second.getRefQosData();
+    remove_ids_not_in_map<QosData>(
+        qos_data_objects, refqos, "QoS Data", pcc.first);
+
     pcc.second.setRefTcData(reftc);
+    pcc.second.setRefQosData(refqos);
   }
 
   // Now parse the decisions (manually as it is not a model)
   for (auto node : policy_decisions) {
     for (auto it = node.begin(); it != node.end(); ++it) {
       SmPolicyDecision decision = decision_from_rules(
-          it->second, pcc_rule_objects, traffic_control_objects);
+          it->second, pcc_rule_objects, traffic_control_objects,
+          qos_data_objects);
       if (!decision.pccRulesIsSet()) {
         Logger::pcf_app().warn(
             "Decision %s could not be parsed. It is ignored",
@@ -156,7 +163,8 @@ bool policy_provisioning_file::read_all_policy_files() {
 
 SmPolicyDecision policy_provisioning_file::decision_from_rules(
     const YAML::Node& node, const std::map<std::string, PccRule>& pcc_rules,
-    const std::map<std::string, TrafficControlData>& traffic_control) {
+    const std::map<std::string, TrafficControlData>& traffic_control,
+    const std::map<std::string, QosData>& qos_data) {
   SmPolicyDecision decision = {};
 
   std::map<std::string, PccRule> used_rules;
@@ -187,7 +195,9 @@ SmPolicyDecision policy_provisioning_file::decision_from_rules(
     return {};
   }
   std::map<std::string, TrafficControlData> used_traffic_control;
-  // now add the TrafficControlDescriptions
+  std::map<std::string, QosData> used_qos_data;
+
+  // now add the TrafficControlDescriptions and QosData
   for (const auto& rule : used_rules) {
     for (const auto& traffic : rule.second.getRefTcData()) {
       auto found = traffic_control.find(traffic);
@@ -195,63 +205,84 @@ SmPolicyDecision policy_provisioning_file::decision_from_rules(
         used_traffic_control.insert(std::make_pair(traffic, found->second));
       }
     }
+    for (const auto& qos : rule.second.getRefQosData()) {
+      auto found = qos_data.find(qos);
+      if (found != qos_data.end()) {
+        used_qos_data.insert(std::make_pair(qos, found->second));
+      }
+    }
   }
   if (!used_rules.empty()) {
     decision.setPccRules(used_rules);
     decision.setTraffContDecs(used_traffic_control);
+    decision.setQosDecs(used_qos_data);
     return decision;
   }
   return {};
 }
 
-void policy_provisioning_file::replace_json_string_with_int(nlohmann::json& j) {
-  for (const auto& elem : j.items()) {
-    if (elem.value().is_primitive()) {
-      try {
-        std::string e = elem.value();
-        if (e == "true") j[elem.key()] = true;
-        if (e == "false") j[elem.key()] = false;
-        int val       = std::stoi(e);
-        j[elem.key()] = val;  // replace with int
-      } catch (std::invalid_argument& ex) {
-      }
-    } else {
-      replace_json_string_with_int(elem.value());
-    }
-  }
-}
-
 template<class T>
 std::map<std::string, T> policy_provisioning_file::convert_yaml_to_model(
     const std::vector<YAML::Node>& nodes) {
   std::map<std::string, T> objects_map;
-  // here we convert YAML to json so we can use the already existing JSON parser
-  // https://stackoverflow.com/questions/43902941/emitting-json-with-yaml-cpp
   for (const auto& node : nodes) {
-    YAML::Emitter emitter;
-    emitter << YAML::DoubleQuoted << YAML::Flow << YAML::BeginSeq << node;
-    std::string json_string(emitter.c_str() + 1);
-    nlohmann::json j = nlohmann::json::parse(json_string);
-    // this is a bit hacky but the problem is that YAML emits ints as strings
-    replace_json_string_with_int(j);
+    auto j = oai::utils::conversions::yaml_to_json(node);
 
     for (const auto& elem : j.items()) {
       T obj;
-      from_json(elem.value(), obj);
-      std::stringstream stream;
-      if (obj.validate(stream)) {
-        objects_map.insert(std::make_pair(elem.key(), obj));
-        Logger::pcf_app().debug(
-            "Rule %s successfully parsed.", elem.key().c_str());
-      } else {
+      try {
+        from_json(elem.value(), obj);
+        std::stringstream stream;
+        if (obj.validate(stream)) {
+          objects_map.insert(std::make_pair(elem.key(), obj));
+          Logger::pcf_app().debug(
+              "Rule %s successfully parsed.", elem.key().c_str());
+        } else {
+          Logger::pcf_app().warn(
+              "Error while parsing rule %s: %s", elem.key(),
+              stream.str().c_str());
+        }
+      } catch (nlohmann::json::exception& e) {
         Logger::pcf_app().warn(
-            "Error while parsing rules: %s", stream.str().c_str());
+            "Error while parsing rule %s: %s", elem.key(), e.what());
       }
     }
   }
   return objects_map;
 }
 
+template<class T>
+void policy_provisioning_file::remove_ids_not_in_map(
+    const std::map<std::string, T>& map, std::vector<std::string>& ids,
+    const std::string& error_msg_type, const std::string& pcc_id) {
+  auto it = ids.begin();
+  while (it != ids.end()) {
+    auto traffic_exists = map.find(*it);
+    if (traffic_exists == map.end()) {
+      Logger::pcf_app().warn(
+          "You have referenced %s ID %s in PCC Rule %s, but it "
+          "does not exist. It is removed from this PCC rule",
+          error_msg_type, *it, pcc_id);
+      it = ids.erase(it);
+    } else {
+      ++it;
+    }
+  }
+}
+
+template<class T>
+std::map<std::string, T> policy_provisioning_file::extract_existing_ids(
+    const std::map<std::string, T>& map, const std::vector<std::string>& ids) {
+  std::map<std::string, T> used_elements;
+  for (const auto& id : ids) {
+    auto found = map.find(id);
+    if (found != map.end()) {
+      used_elements.insert(std::make_pair(id, found->second));
+    }
+  }
+  return used_elements;
+}
+
 bool policy_provisioning_file::read_all_files_in_dir(
     const std::string& dir_path, std::vector<YAML::Node>& yaml_output) {
   if (!exists(dir_path)) {
diff --git a/src/pcf_app/sm_policy/policy_provisioning_file.hpp b/src/pcf_app/sm_policy/policy_provisioning_file.hpp
index 912f8c9d828fd466c8a5611b287860fcc93c58b3..bc349cf58a85d4c8c6b433d2cd63892174ae249f 100644
--- a/src/pcf_app/sm_policy/policy_provisioning_file.hpp
+++ b/src/pcf_app/sm_policy/policy_provisioning_file.hpp
@@ -57,13 +57,21 @@ class policy_provisioning_file {
       const YAML::Node& node,
       const std::map<std::string, oai::pcf::model::PccRule>& pcc_rules,
       const std::map<std::string, oai::pcf::model::TrafficControlData>&
-          traffic_control);
+          traffic_control,
+      const std::map<std::string, oai::pcf::model::QosData>& qos_data);
 
   template<class T>
   static std::map<std::string, T> convert_yaml_to_model(
       const std::vector<YAML::Node>& nodes);
 
-  static void replace_json_string_with_int(nlohmann::json& j);
+  template<class T>
+  static void remove_ids_not_in_map(
+      const std::map<std::string, T>& map, std::vector<std::string>& ids,
+      const std::string& error_msg_type, const std::string& pcc_id);
+
+  template<class T>
+  static std::map<std::string, T> extract_existing_ids(
+      const std::map<std::string, T>& map, const std::vector<std::string>& ids);
 
   std::shared_ptr<oai::pcf::app::sm_policy::policy_storage> m_policy_storage;
 };