diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py
index 62d5380b56803b3cc21dd1456292ec9df470cb15..cd6819999a585b541ef716fa481910ae3b53ecbf 100644
--- a/src/context/service/database/models/enums/ServiceType.py
+++ b/src/context/service/database/models/enums/ServiceType.py
@@ -29,6 +29,7 @@ class ORM_ServiceTypeEnum(enum.Enum):
     TE                        = ServiceTypeEnum.SERVICETYPE_TE
     E2E                       = ServiceTypeEnum.SERVICETYPE_E2E
     OPTICAL_CONNECTIVITY      = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY
+    QKD                       = ServiceTypeEnum.SERVICETYPE_QKD
 
 grpc_to_enum__service_type = functools.partial(
     grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index ca70fa9386e356e7e49397365701013e1d3a1697..494e59e3c6698c6ae052e1f4b35b7a56e8dc3fba 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -26,7 +26,8 @@ SERVICE_TYPE_VALUES = {
     ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
     ServiceTypeEnum.SERVICETYPE_TE,
     ServiceTypeEnum.SERVICETYPE_E2E,
-    ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY
+    ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY,
+    ServiceTypeEnum.SERVICETYPE_QKD,
 }
 
 DEVICE_DRIVER_VALUES = {
@@ -41,7 +42,8 @@ DEVICE_DRIVER_VALUES = {
     DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
     DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS,
     DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN,
-    DeviceDriverEnum.DEVICEDRIVER_OC
+    DeviceDriverEnum.DEVICEDRIVER_OC,
+    DeviceDriverEnum.DEVICEDRIVER_QKD,
 }
 
 # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 8b5e2b2834f37dc3d616907e46cf1fa5b2f1274f..3beb7c9ee95c79d7219550a62166c648e061a01d 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -27,6 +27,7 @@ from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
 from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler
 from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler
 from .oc.OCServiceHandler import OCServiceHandler
+from .qkd.qkd_service_handler import QKDServiceHandler
 
 SERVICE_HANDLERS = [
     (L2NMEmulatedServiceHandler, [
@@ -106,5 +107,11 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY,
             FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC,
         }
+    ]),
+    (QKDServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_QKD,
+            FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_QKD],
+        }
     ])
 ]
diff --git a/src/service/service/service_handlers/qkd/__init__.py b/src/service/service/service_handlers/qkd/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- /dev/null
+++ b/src/service/service/service_handlers/qkd/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/src/service/service/service_handlers/qkd/qkd_service_handler.py b/src/service/service/service_handlers/qkd/qkd_service_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..76c67867ee2f4bae60b8dd6e187f221f2efc1eb0
--- /dev/null
+++ b/src/service/service/service_handlers/qkd/qkd_service_handler.py
@@ -0,0 +1,394 @@
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import json, logging, uuid
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
+from common.proto.app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+
+LOGGER = logging.getLogger(__name__)
+
+def get_endpoint_name_by_uuid(device, uuid):
+    for device_endpoint in device.device_endpoints:
+        if device_endpoint.endpoint_id.endpoint_uuid.uuid == uuid:
+            return device_endpoint.name
+    return None
+
+class QKDServiceHandler(_ServiceHandler):
+    def __init__(   # pylint: disable=super-init-not-called
+        self, service : Service, task_executor : TaskExecutor, **settings
+    ) -> None:
+        self.__service = service
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
+
+
+    # Optare: This function is where the service is created
+    # Optare: It already receives the path provided by pathcomp in endpoints variable
+    # Optare: It then checks for each respective QKD Node and requests SBI to inform of the new connection
+    # Optare: It also requests app module for a creation of internal service if the service is virtual
+    def SetEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]],
+        connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) < 2 or len(endpoints) % 2: return []
+
+        LOGGER.info('Endpoints: ' + str(endpoints))
+        
+
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        settings = self.__settings_handler.get('/settings')
+
+        context_uuid = self.__service.service_id.context_id.context_uuid.uuid
+
+        results = []
+        try:
+
+            if len(endpoints) > 4:
+                is_virtual = True
+            else:
+                is_virtual = False
+            
+            devices    = []
+            qkdn_ids   = []
+            interfaces = []
+            links      = []
+
+            # Optare: First a big iteration through all devices is done in order to obtain all information needed for the whole operation
+            # Optare: This is a way to minimize time of operation. Otherwise it would require many O(N) operations. This way we can reduce it to one
+
+            # Populate devices and QKDN ids
+            for idx, endpoint in enumerate(endpoints[::2]):
+                device_uuid, endpoint_left_uuid = get_device_endpoint_uuids(endpoint)
+                _, endpoint_right_uuid = get_device_endpoint_uuids(endpoints[2 * idx + 1])
+
+                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                devices.append(device)
+                interfaces.append([0,0])
+                links.append([])
+
+
+
+
+
+                endpoint_left =  get_endpoint_name_by_uuid(device, endpoint_left_uuid) if idx > 0 else None
+                endpoint_right = get_endpoint_name_by_uuid(device, endpoint_right_uuid) if 2 * idx + 2 < len(endpoints) else None
+
+
+                for config_rule in device.device_config.config_rules:
+                    resource_key = config_rule.custom.resource_key
+
+                    if resource_key == '__node__':
+                        value = json.loads(config_rule.custom.resource_value)
+                        qkdn_ids.append(value['qkdn_id'])
+                    
+                    elif resource_key.startswith('/interface'):
+                        value = json.loads(config_rule.custom.resource_value)
+                        try:
+                            endpoint_str = value['qkdi_att_point']['uuid']
+                            LOGGER.info("A: " + str(endpoint_str) + "....." + str(endpoint_left) + "....." + str(endpoint_right))
+
+                            if endpoint_str == endpoint_left:
+                                interfaces[idx][0] = value['qkdi_id']
+                            elif endpoint_str == endpoint_right:
+                                interfaces[idx][1] = value['qkdi_id']
+                        except KeyError:
+                            pass
+                    
+                    elif resource_key.startswith('/link'):
+                        value = json.loads(config_rule.custom.resource_value)
+                        links[idx].append(( value['uuid'],
+                            (value['src_qkdn_id'], value['src_interface_id']), 
+                            (value['dst_qkdn_id'], value['dst_interface_id'])
+                        ))
+
+
+            LOGGER.info("IFs: " + str(interfaces))
+            LOGGER.info("Links: " + str(links))
+            LOGGER.info("context_: " + context_uuid)
+
+
+            # Optare: From here now is where the work is really done. It iterates over every device in use for the service (ordered)
+
+            src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0])
+            src_device = devices[0]
+            src_endpoint = get_endpoint_matching(src_device, src_endpoint_uuid)
+
+            dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1])
+            dst_device = devices[-1]
+            dst_endpoint = get_endpoint_matching(dst_device, dst_endpoint_uuid)
+
+            src_qkdn_id = qkdn_ids[0]
+            dst_qkdn_id = qkdn_ids[-1]
+
+            src_interface_id = interfaces[0][1]
+            dst_interface_id = interfaces[-1][0]
+
+            service_qkdl_id_src_dst = str(uuid.uuid4())
+            service_qkdl_id_dst_src = str(uuid.uuid4())
+
+            
+            for idx, device in enumerate(devices):
+
+                # Even though we always create them together. There is a chance the admin deletes one of the rules manually
+                phys_qkdl_id_right = None if idx == (len(devices) - 1) else '' # None == impossible
+                phys_qkdl_id_left = None if idx == 0 else '' # None == impossible
+
+                for link_uuid, link_src, link_dst in links[idx]:
+                    qkdn_link_src, qkdn_interface_src = link_src
+                    qkdn_link_dst, qkdn_interface_dst = link_dst
+
+                    if phys_qkdl_id_right == '' and \
+                        qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][1] and \
+                        qkdn_link_dst[idx+1] and qkdn_interface_dst == interfaces[idx+1][0]:
+                        phys_qkdl_id_right = link_uuid
+
+
+                    if phys_qkdl_id_left == '' and \
+                        qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][0] and \
+                        qkdn_link_dst[idx-1] and qkdn_interface_dst == interfaces[idx-1][1]:
+                        phys_qkdl_id_left = link_uuid
+
+
+                # Optare: Before adding information to config_rules you have to delete the list first otherwise old content will be called again
+                del device.device_config.config_rules[:]
+
+
+                if phys_qkdl_id_right:
+                    if not is_virtual:
+                        service_qkdl_id_src_dst = phys_qkdl_id_right
+
+                elif phys_qkdl_id_right == '':
+                    qkdl_id_src_dst = str(uuid.uuid4()) if is_virtual else service_qkdl_id_src_dst
+
+                    json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_src_dst), {
+                        'uuid'               : qkdl_id_src_dst,
+                        'type'               : 'DIRECT',
+                        'src_qkdn_id'        : qkdn_ids[idx],
+                        'src_interface_id'   : interfaces[idx][1],
+                        'dst_qkdn_id'        : qkdn_ids[idx+1],
+                        'dst_interface_id'   : interfaces[idx+1][0],
+                    })
+
+                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
+
+
+                if phys_qkdl_id_left:
+                    if not is_virtual:
+                        service_qkdl_id_dst_src = phys_qkdl_id_left
+
+                elif phys_qkdl_id_left == '':
+                    qkdl_id_dst_src = str(uuid.uuid4()) if is_virtual else service_qkdl_id_dst_src
+
+                    json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_dst_src), {
+                        'uuid'               : qkdl_id_dst_src,
+                        'type'               : 'DIRECT',
+                        'src_qkdn_id'        : qkdn_ids[idx],
+                        'src_interface_id'   : interfaces[idx][0],
+                        'dst_qkdn_id'        : qkdn_ids[idx-1],
+                        'dst_interface_id'   : interfaces[idx-1][1],
+                    })
+
+                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                
+
+
+                if is_virtual:
+                    if idx < len(qkdn_ids) - 1:
+                        json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_src_dst), {
+                            'uuid'               : service_qkdl_id_src_dst,
+                            'type'               : 'VIRTUAL',
+                            'src_qkdn_id'        : src_qkdn_id,
+                            'src_interface_id'   : src_interface_id,
+                            'dst_qkdn_id'        : dst_qkdn_id,
+                            'dst_interface_id'   : dst_interface_id,
+                            'virt_prev_hop'      : qkdn_ids[idx-1] if idx > 0 else None,
+                            'virt_next_hops'     : qkdn_ids[idx+1:],
+                            'virt_bandwidth'     : 0,
+                        })
+
+                        device.device_config.config_rules.append(ConfigRule(**json_config_rule))
+
+                    if idx > 0:
+                        json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_dst_src), {
+                            'uuid'               : service_qkdl_id_dst_src,
+                            'type'               : 'VIRTUAL',
+                            'src_qkdn_id'        : dst_qkdn_id,
+                            'src_interface_id'   : dst_interface_id,
+                            'dst_qkdn_id'        : src_qkdn_id,
+                            'dst_interface_id'   : src_interface_id,
+                            'virt_prev_hop'      : qkdn_ids[idx+1] if idx < len(qkdn_ids) - 1 else None,
+                            'virt_next_hops'     : qkdn_ids[idx-1::-1],
+                            'virt_bandwidth'     : 0,
+                        })
+
+                        device.device_config.config_rules.append(ConfigRule(**json_config_rule))
+
+
+
+                json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), {
+                    'uuid'               : service_uuid,
+                    'qkdl_id_src_dst'    : service_qkdl_id_src_dst,
+                    'qkdl_id_dst_src'    : service_qkdl_id_dst_src,
+                })
+
+                device.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device)
+
+
+            if is_virtual:
+
+                # Register App
+                internal_app_src_dst = {
+                    'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}},
+                    'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON,
+                    'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL,
+                    'server_app_id': '',
+                    'client_app_id': [],
+                    'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_src_dst}}],
+                    'local_device_id': src_device.device_id,
+                    'remote_device_id': dst_device.device_id,
+                }
+
+                self.__task_executor.register_app(App(**internal_app_src_dst))
+                
+
+                # Register App
+                internal_app_dst_src = {
+                    'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}},
+                    'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON,
+                    'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL,
+                    'server_app_id': '',
+                    'client_app_id': [],
+                    'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_dst_src}}],
+                    'local_device_id': dst_device.device_id,
+                    'remote_device_id': src_device.device_id,
+                }
+
+                self.__task_executor.register_app(App(**internal_app_dst_src))
+
+            results.append(True)
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
+            results.append(e)
+
+        return results
+
+    # Optare: This will be to delete a service
+    def DeleteEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]],
+        connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        """ Delete service endpoints form a list.
+            Parameters:
+                endpoints: List[Tuple[str, str, Optional[str]]]
+                    List of tuples, each containing a device_uuid,
+                    endpoint_uuid, and the topology_uuid of the endpoint
+                    to be removed.
+                connection_uuid : Optional[str]
+                    If specified, is the UUID of the connection this endpoint is associated to.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for endpoint deletions requested.
+                    Return values must be in the same order as the requested
+                    endpoints. If an endpoint is properly deleted, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        raise NotImplementedError()
+
+    # Optare: Can be ingored. It's in case if a service is later updated. Not required to proper functioning
+
+    def SetConstraint(self, constraints: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Create/Update service constraints.
+            Parameters:
+                constraints: List[Tuple[str, Any]]
+                    List of tuples, each containing a constraint_type and the
+                    new constraint_value to be set.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for constraint changes requested.
+                    Return values must be in the same order as the requested
+                    constraints. If a constraint is properly set, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        raise NotImplementedError()
+
+    def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Delete service constraints.
+            Parameters:
+                constraints: List[Tuple[str, Any]]
+                    List of tuples, each containing a constraint_type pointing
+                    to the constraint to be deleted, and a constraint_value
+                    containing possible additionally required values to locate
+                    the constraint to be removed.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for constraint deletions requested.
+                    Return values must be in the same order as the requested
+                    constraints. If a constraint is properly deleted, True must
+                    be returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        raise NotImplementedError()
+
+    def SetConfig(self, resources: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Create/Update configuration for a list of service resources.
+            Parameters:
+                resources: List[Tuple[str, Any]]
+                    List of tuples, each containing a resource_key pointing to
+                    the resource to be modified, and a resource_value
+                    containing the new value to be set.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for resource key changes requested.
+                    Return values must be in the same order as the requested
+                    resource keys. If a resource is properly set, True must be
+                    returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        raise NotImplementedError()
+
+    def DeleteConfig(self, resources: List[Tuple[str, Any]]) \
+            -> List[Union[bool, Exception]]:
+        """ Delete configuration for a list of service resources.
+            Parameters:
+                resources: List[Tuple[str, Any]]
+                    List of tuples, each containing a resource_key pointing to
+                    the resource to be modified, and a resource_value containing
+                    possible additionally required values to locate the value
+                    to be removed.
+            Returns:
+                results: List[Union[bool, Exception]]
+                    List of results for resource key deletions requested.
+                    Return values must be in the same order as the requested
+                    resource keys. If a resource is properly deleted, True must
+                    be returned; otherwise, the Exception that is raised during
+                    the processing must be returned.
+        """
+        raise NotImplementedError()