diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py
index 32b99a31f22072874ab894de2a87ce2b7d56ba85..b05821c7814ce250abca1819b111376af7c0430f 100644
--- a/src/common/tools/object_factory/Service.py
+++ b/src/common/tools/object_factory/Service.py
@@ -42,6 +42,16 @@ def json_service(
         'service_config'      : {'config_rules': copy.deepcopy(config_rules)},
     }
 
+def json_service_qkd_planned(
+        service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [],
+        config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_NAME
+    ):
+
+    return json_service(
+        service_uuid, ServiceTypeEnum.SERVICETYPE_QKD, context_id=json_context_id(context_uuid),
+        status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
+        config_rules=config_rules)
+
 def json_service_l2nm_planned(
         service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [],
         config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_NAME
diff --git a/src/device/tests/qkd/unit/test_qkd_mock_connectivity.py b/src/device/tests/qkd/unit/test_qkd_mock_connectivity.py
index 150d00fd079b0a036f383653c833562279bb4d72..be9427d9b619423a61a3e6f5270d8aab76dc8955 100644
--- a/src/device/tests/qkd/unit/test_qkd_mock_connectivity.py
+++ b/src/device/tests/qkd/unit/test_qkd_mock_connectivity.py
@@ -38,3 +38,4 @@ def test_qkd_driver_timeout_connection(mock_get, qkd_driver):
     mock_get.side_effect = requests.exceptions.Timeout
     qkd_driver.timeout = 0.001  # Simulate very short timeout
     assert qkd_driver.Connect() is False
+
diff --git a/src/webui/Dockerfile b/src/webui/Dockerfile
index 55e67b670f36812a55cf60e411cf137bc5b8a2ee..204b6f7311d4d182d830ef5483204790e165599b 100644
--- a/src/webui/Dockerfile
+++ b/src/webui/Dockerfile
@@ -84,6 +84,8 @@ COPY --chown=webui:webui src/service/__init__.py service/__init__.py
 COPY --chown=webui:webui src/service/client/. service/client/
 COPY --chown=webui:webui src/slice/__init__.py slice/__init__.py
 COPY --chown=webui:webui src/slice/client/. slice/client/
+COPY --chown=webui:webui src/app/__init__.py app/__init__.py
+COPY --chown=webui:webui src/app/client/. app/client/
 COPY --chown=webui:webui src/webui/. webui/
 COPY --chown=webui:webui src/bgpls_speaker/__init__.py bgpls_speaker/__init__.py
 COPY --chown=webui:webui src/bgpls_speaker/client/. bgpls_speaker/client/
diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py
index b864d3549e051b54e888c80547724da14fec5f67..990792069597f9116ad999487a8982708e978d40 100644
--- a/src/webui/service/__init__.py
+++ b/src/webui/service/__init__.py
@@ -19,6 +19,7 @@ from flask_healthz import healthz, HealthError
 from common.tools.grpc.Tools import grpc_message_to_json
 from context.client.ContextClient import ContextClient
 from device.client.DeviceClient import DeviceClient
+from qkd_app.client.QKDAppClient import QKDAppClient
 
 def get_working_context() -> str:
     return session['context_uuid'] if 'context_uuid' in session else '---'
@@ -37,6 +38,10 @@ def readiness():
         device_client = DeviceClient()
         device_client.connect()
         device_client.close()
+        # DEPENDENCY QKD
+        qkd_app_client = QKDAppClient()
+        qkd_app_client.connect()
+        qkd_app_client.close()
     except Exception as e:
         raise HealthError("Can't connect with the service: {:s}".format(str(e))) from e
 
@@ -102,6 +107,9 @@ def create_app(use_config=None, web_app_root=None):
     from webui.service.link.routes import link              # pylint: disable=import-outside-toplevel
     app.register_blueprint(link)
 
+    from webui.service.qkd_app.routes import qkd_app as _qkd_app             # pylint: disable=import-outside-toplevel
+    app.register_blueprint(_qkd_app)
+
     from webui.service.policy_rule.routes import policy_rule # pylint: disable=import-outside-toplevel
     app.register_blueprint(policy_rule)
 
diff --git a/src/webui/service/__main__.py b/src/webui/service/__main__.py
index e9a906e8a431e287911547abc4065d9d9364ccb4..8cb577d7149f08fa05c11961945f2b8d98beaa00 100644
--- a/src/webui/service/__main__.py
+++ b/src/webui/service/__main__.py
@@ -33,6 +33,7 @@ def main():
     logging.basicConfig(level=log_level)
     logger = logging.getLogger(__name__)
 
+    # DEPENDENCY QKD
     wait_for_environment_variables([
         get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST     ),
         get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
@@ -42,6 +43,8 @@ def main():
         get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
         get_env_var_name(ServiceNameEnum.SLICE,   ENVVAR_SUFIX_SERVICE_HOST     ),
         get_env_var_name(ServiceNameEnum.SLICE,   ENVVAR_SUFIX_SERVICE_PORT_GRPC),
+        get_env_var_name(ServiceNameEnum.APP,   ENVVAR_SUFIX_SERVICE_HOST     ),
+        get_env_var_name(ServiceNameEnum.APP,   ENVVAR_SUFIX_SERVICE_PORT_GRPC),
     ])
 
     logger.info('Starting...')
diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py
index e4c71d92170dc9fe46996a1c93978647800aa300..eebc06755f204cd270ff8feca21733cb4426493a 100644
--- a/src/webui/service/device/forms.py
+++ b/src/webui/service/device/forms.py
@@ -33,6 +33,7 @@ class AddDeviceForm(FlaskForm):
     device_drivers_gnmi_openconfig = BooleanField('GNMI OPENCONFIG')
     device_drivers_optical_tfs = BooleanField('OPTICAL TFS')
     device_drivers_ietf_actn = BooleanField('IETF ACTN')
+    device_drivers_qkd = BooleanField('QKD')
 
     device_config_address = StringField('connect/address',default='127.0.0.1',validators=[DataRequired(), Length(min=5)])
     device_config_port = StringField('connect/port',default='0',validators=[DataRequired(), Length(min=1)])
@@ -57,3 +58,4 @@ class UpdateDeviceForm(FlaskForm):
                            validators=[NumberRange(min=0)])
                         
     submit = SubmitField('Update')
+    
diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py
index b7fdb78e85dc634627de02947c0861a7f13bdae9..429f4a2ea8539b7b12baf5e20eb30760694ede64 100644
--- a/src/webui/service/device/routes.py
+++ b/src/webui/service/device/routes.py
@@ -129,6 +129,8 @@ def add():
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS)
         if form.device_drivers_ietf_actn.data:
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN)
+        if form.device_drivers_qkd.data:
+            device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_QKD)
         device_obj.device_drivers.extend(device_drivers) # pylint: disable=no-member
 
         try:
diff --git a/src/webui/service/qkd_app/__init__.py b/src/webui/service/qkd_app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbfc943b68af13a11e562abbc8680ade71db8f02
--- /dev/null
+++ b/src/webui/service/qkd_app/__init__.py
@@ -0,0 +1,13 @@
+# 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/webui/service/qkd_app/routes.py b/src/webui/service/qkd_app/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..5cbe715dcfe6de16d84706b28799e22018737f29
--- /dev/null
+++ b/src/webui/service/qkd_app/routes.py
@@ -0,0 +1,113 @@
+# 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 grpc, json, logging
+
+from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for
+from common.proto.context_pb2 import Empty, Link, LinkId, LinkList
+from common.proto.qkd_app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum
+from common.tools.context_queries.Context import get_context
+from common.tools.context_queries.Device import get_device
+from common.tools.context_queries.Topology import get_topology
+from context.client.ContextClient import ContextClient
+from qkd_app.client.QKDAppClient import QKDAppClient
+
+
+LOGGER = logging.getLogger(__name__)
+app = Blueprint('qkd_app', __name__, url_prefix='/qkd_app')
+
+qkd_app_client = QKDAppClient()
+context_client = ContextClient()
+
+@app.get('/')
+def home():
+    if 'context_uuid' not in session or 'topology_uuid' not in session:
+        flash("Please select a context!", "warning")
+        return redirect(url_for("main.home"))
+    context_uuid = session['context_uuid']
+    topology_uuid = session['topology_uuid']
+
+    context_client.connect()
+    device_names = dict()
+
+    context_obj = get_context(context_client, context_uuid, rw_copy=False)
+    if context_obj is None:
+        flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
+        apps = list()
+    else:
+        try:
+            apps = qkd_app_client.ListApps(context_obj.context_id)
+            apps = apps.apps
+        except grpc.RpcError as e:
+            if e.code() != grpc.StatusCode.NOT_FOUND: raise
+            if e.details() != 'Context({:s}) not found'.format(context_uuid): raise
+            apps = list()
+        else:
+            # Too many requests to context_client if it has too many apps (update in the future)
+            for app in apps:
+                if app.local_device_id.device_uuid.uuid not in device_names:
+                    device = get_device(context_client, app.local_device_id.device_uuid.uuid)
+                    if device is not None:
+                        device_names[app.local_device_id.device_uuid.uuid] = device.name
+                
+                if app.remote_device_id.device_uuid.uuid and app.remote_device_id.device_uuid.uuid not in device_names:
+                    device = get_device(context_client, app.remote_device_id.device_uuid.uuid)
+                    if device is not None:
+                        device_names[app.remote_device_id.device_uuid.uuid] = device.name
+
+    context_client.close()
+    return render_template(
+        'app/home.html', apps=apps, device_names=device_names, ate=QKDAppTypesEnum, ase=QKDAppStatusEnum)
+
+
+@app.route('detail/<path:app_uuid>', methods=('GET', 'POST'))
+def detail(app_uuid: str):
+    '''
+    context_client.connect()
+    link_obj = get_link(context_client, link_uuid, rw_copy=False)
+    if link_obj is None:
+        flash('Link({:s}) not found'.format(str(link_uuid)), 'danger')
+        link_obj = Link()
+        device_names, endpoints_data = dict(), dict()
+    else:
+        device_names, endpoints_data = get_endpoint_names(context_client, link_obj.link_endpoint_ids)
+    context_client.close()
+    return render_template('link/detail.html',link=link_obj, device_names=device_names, endpoints_data=endpoints_data)
+    '''
+    pass
+
+@app.get('<path:app_uuid>/delete')
+def delete(app_uuid):
+    '''
+    try:
+
+        # first, check if link exists!
+        # request: LinkId = LinkId()
+        # request.link_uuid.uuid = link_uuid
+        # response: Link = client.GetLink(request)
+        # TODO: finalize implementation
+
+        request = LinkId()
+        request.link_uuid.uuid = link_uuid # pylint: disable=no-member
+        context_client.connect()
+        context_client.RemoveLink(request)
+        context_client.close()
+
+        flash(f'Link "{link_uuid}" deleted successfully!', 'success')
+    except Exception as e: # pylint: disable=broad-except
+        flash(f'Problem deleting link "{link_uuid}": {e.details()}', 'danger')
+        current_app.logger.exception(e)
+    return redirect(url_for('link.home'))
+    '''
+    pass
diff --git a/src/webui/service/service/__init__.py b/src/webui/service/service/__init__.py
index 3ee6f7071f145e06c3aeaefc09a43ccd88e619e3..5cf553eaaec41de7599b6723e31e4ca3f82cbcae 100644
--- a/src/webui/service/service/__init__.py
+++ b/src/webui/service/service/__init__.py
@@ -12,3 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
diff --git a/src/webui/service/service/forms.py b/src/webui/service/service/forms.py
index f07acf54365b79245583e7f9567b8bc4a5cfd89d..dad15f1c2dbef3a5d1c9a3ecdc6f96c00b883aa2 100644
--- a/src/webui/service/service/forms.py
+++ b/src/webui/service/service/forms.py
@@ -17,6 +17,11 @@ from flask_wtf import FlaskForm
 from wtforms import StringField, SelectField, IntegerField, DecimalField
 from wtforms.validators import InputRequired, Optional, NumberRange, ValidationError, StopValidation
 
+# Custom uuid validator
+def validate_uuid_address(form, field):
+    if not re.match(r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$', field.data):
+        raise ValidationError('Invalid uuid format')
+
 # Custom IPv4 address validator
 def validate_ipv4_address(form, field):
     try:
@@ -60,7 +65,25 @@ class CustomInputRequired():
             raise StopValidation(self.message)
         
 class AddServiceForm_1(FlaskForm):
-    service_type = SelectField('Type of service', choices=[('', 'Select a type of service to add'), ('ACL_L2', 'ACL_L2'), ('ACL_IPV4', 'ACL_IPV4'), ('ACL_IPV6', 'ACL_IPV6'), ('L2VPN', 'L2VPN'), ('L3VPN', 'L3VPN')], validators=[InputRequired()])
+    service_type = SelectField('Type of service', choices=[('', 'Select a type of service to add'), ('ACL_L2', 'ACL_L2'), ('ACL_IPV4', 'ACL_IPV4'), ('ACL_IPV6', 'ACL_IPV6'), ('L2VPN', 'L2VPN'), ('L3VPN', 'L3VPN'), ('QKD', 'QKD')], validators=[InputRequired()])
+
+class AddServiceForm_QKD(FlaskForm):
+    #GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
+    service_name       = StringField('Service Name', validators=[CustomInputRequired()])
+    service_type       = SelectField('Service Type', choices=[(6, '6 (QKD)')], validators=[CustomInputRequired()])
+    service_device_1   = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()])
+    service_device_2   = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()])
+    service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()])
+    service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()])
+    
+    #GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
+    service_capacity    = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)])
+    service_latency     = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)])
+    service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)])
+    service_isolation   = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), 
+                                                                    ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), 
+                                                                    ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), 
+                                                                    ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
 
 class AddServiceForm_ACL_L2(FlaskForm):
     #GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
@@ -259,3 +282,4 @@ class AddServiceForm_L3VPN(FlaskForm):
     Device_2_IF_address_ip  = StringField('Device_2 IP Address', validators=[CustomInputRequired(), validate_ipv4_address])
     Device_2_IF_address_prefix = IntegerField('Device_2 IP Prefix length', validators=[CustomInputRequired(), validate_uint32])
     Device_2_IF_description = StringField ('Device_2 SubIF Description', validators=[Optional()])
+    
diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py
index 92025b2bec4f7c70b446a8c422e2cdb166c95466..c164b41773e15ac4e9746753e1fdc3b56a51b0d2 100644
--- a/src/webui/service/service/routes.py
+++ b/src/webui/service/service/routes.py
@@ -35,14 +35,14 @@ from common.tools.object_factory.Constraint import (
 from common.tools.object_factory.Context import json_context_id
 from common.tools.object_factory.Device import json_device_id
 from common.tools.object_factory.EndPoint import json_endpoint_id
-from common.tools.object_factory.Service import json_service_l2nm_planned, json_service_l3nm_planned
+from common.tools.object_factory.Service import json_service_l2nm_planned, json_service_l3nm_planned, json_service_qkd_planned
 from common.tools.object_factory.Topology import json_topology_id
 from context.client.ContextClient import ContextClient
 from device.client.DeviceClient import DeviceClient
 from service.client.ServiceClient import ServiceClient
 from webui.service.service.forms import (
     AddServiceForm_1, AddServiceForm_ACL_L2, AddServiceForm_ACL_IPV4, AddServiceForm_ACL_IPV6,
-    AddServiceForm_L2VPN, AddServiceForm_L3VPN
+    AddServiceForm_L2VPN, AddServiceForm_L3VPN, AddServiceForm_QKD
 )
 
 LOGGER = logging.getLogger(__name__)
@@ -329,10 +329,83 @@ def add_configure():
     form_1 = AddServiceForm_1()
     if form_1.validate_on_submit():
         service_type = str(form_1.service_type.data)
-        if service_type in {'ACL_L2', 'ACL_IPV4', 'ACL_IPV6', 'L2VPN', 'L3VPN'}:
+        if service_type in {'ACL_L2', 'ACL_IPV4', 'ACL_IPV6', 'L2VPN', 'L3VPN', 'QKD'}:
             return redirect(url_for('service.add_configure_{:s}'.format(service_type)))
     return render_template('service/add.html', form_1=form_1, submit_text='Continue to configuraton')
 
+@service.route('add/configure/QKD', methods=['GET', 'POST'])
+def add_configure_QKD():
+    form_qkd = AddServiceForm_QKD()
+    service_obj = Service()
+
+    context_uuid, topology_uuid = get_context_and_topology_uuids()
+    if context_uuid and topology_uuid:
+        context_client.connect()
+        grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False)
+        if grpc_topology:
+            topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}          
+            devices = get_filtered_devices(context_client, topo_device_uuids)
+            grpc_devices = context_client.ListDevices(Empty())                                          
+            devices = [
+                device for device in grpc_devices.devices
+                if device.device_id.device_uuid.uuid in topo_device_uuids and DeviceDriverEnum.DEVICEDRIVER_QKD in device.device_drivers
+            ]
+            choices = get_device_choices(devices)
+            add_device_choices_to_form(choices, form_qkd.service_device_1)
+            add_device_choices_to_form(choices, form_qkd.service_device_2)
+        else:
+            flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger')
+    else:
+        flash('Missing context or topology UUID', 'danger')
+
+    if form_qkd.validate_on_submit():
+        try:
+            [selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2] = validate_selected_devices_and_endpoints(form_qkd, devices)
+        except Exception as e:
+            flash('{:s}'.format(str(e.args[0])), 'danger')
+            current_app.logger.exception(e)
+            return render_template('service/configure_QKD.html', form_qkd=form_qkd, submit_text='Add New Service')
+        
+        service_uuid, service_type, endpoint_ids = set_service_parameters(service_obj, form_qkd, selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2)
+        constraints = add_constraints(form_qkd)
+        params_device_1_with_data = get_device_params(form_qkd, 1, service_type)
+        params_device_2_with_data = get_device_params(form_qkd, 2, service_type)
+        print(params_device_1_with_data)
+        print(params_device_2_with_data)
+        params_settings = {}
+        config_rules = [
+            json_config_rule_set(
+                    '/settings', params_settings
+                ),
+            json_config_rule_set(
+                '/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_1.name), str(selected_endpoint_1)), params_device_1_with_data
+            ),
+            json_config_rule_set(
+                '/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_2.name), str(selected_endpoint_2)), params_device_2_with_data
+            )
+        ]
+
+        service_client.connect()
+        context_client.connect()
+        device_client.connect()
+        descriptor_json = json_service_qkd_planned(service_uuid = service_uuid, endpoint_ids = endpoint_ids, constraints = constraints, config_rules = config_rules, context_uuid= context_uuid)
+        descriptor_json = {"services": [descriptor_json]}
+        try:
+            process_descriptors(descriptor_json)
+            flash('Service "{:s}" added successfully!'.format(service_obj.service_id.service_uuid.uuid), 'success')
+            return redirect(url_for('service.home', service_uuid=service_obj.service_id.service_uuid.uuid))
+        except Exception as e:
+            flash('Problem adding service: {:s}'.format((str(e.args[0]))), 'danger')
+            current_app.logger.exception(e)
+        finally:
+            context_client.close()                                                                                      
+            device_client.close()
+            service_client.close()
+
+    
+    return render_template('service/configure_QKD.html', form_qkd=form_qkd, submit_text='Add New Service')
+
+
 @service.route('add/configure/ACL_L2', methods=['GET', 'POST'])
 def add_configure_ACL_L2():
     form_acl = AddServiceForm_ACL_L2()
@@ -666,6 +739,9 @@ def get_device_params(form, device_num, form_type):
             'ni_description': str(getattr(form, 'NI_description').data),
             'subif_description': str(getattr(form, f'Device_{device_num}_IF_description').data),
         }
+    elif form_type == 6:
+        device_params = {
+        }
     else:
         raise ValueError(f'Unsupported form type: {form_type}')
 
diff --git a/src/webui/service/static/topology_icons/emu-qkd-node.png b/src/webui/service/static/topology_icons/emu-qkd-node.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4dc1abaf42a56ff07d1f4a2c5d250b56486584d
Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-qkd-node.png differ
diff --git a/src/webui/service/static/topology_icons/qkd-node.png b/src/webui/service/static/topology_icons/qkd-node.png
new file mode 100644
index 0000000000000000000000000000000000000000..79f40d2a600bd7f9e55d0360a132800c09a8ac85
Binary files /dev/null and b/src/webui/service/static/topology_icons/qkd-node.png differ
diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html
index c154346204a4ad59eec54a7e9ae3956a7f3db655..f59e534bdd94b31b246051333e60d380a3e3bc91 100644
--- a/src/webui/service/templates/base.html
+++ b/src/webui/service/templates/base.html
@@ -103,6 +103,13 @@
                   <a class="nav-link" href="{{ url_for('load_gen.home') }}">Load Generator</a>
                   {% endif %}
                 </li>
+                <li class="nav-item">
+                  {% if '/qkd_app/' in request.path %}
+                  <a class="nav-link active" aria-current="page" href="{{ url_for('app.home') }}">App</a>
+                  {% else %}
+                  <a class="nav-link" href="{{ url_for('app.home') }}">App</a>
+                  {% endif %}
+                </li> 
                 <li class="nav-item">
                   {% if '/bgpls/' in request.path %}
                   <a class="nav-link active" aria-current="page" href="{{ url_for('bgpls.home') }}">BGPLS</a>
diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html
index 3bea6ae719a75c91835ceb35f50b5bbeba2c7940..e11c37688c09b96849c63a5d51cd7e546468d558 100644
--- a/src/webui/service/templates/device/add.html
+++ b/src/webui/service/templates/device/add.html
@@ -95,6 +95,7 @@
                 <br />
                 {{ form.device_drivers_optical_tfs }} {{ form.device_drivers_optical_tfs.label(class="col-sm-3 col-form-label") }}
                 {{ form.device_drivers_ietf_actn }} {{ form.device_drivers_ietf_actn.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_qkd }} {{ form.device_drivers_qkd.label(class="col-sm-3 col-form-label") }}
                 {% endif %}
             </div>
         </div>
diff --git a/src/webui/service/templates/qkd_app/home.html b/src/webui/service/templates/qkd_app/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..e138acd1463202b64114921d70f1df3e3716d40a
--- /dev/null
+++ b/src/webui/service/templates/qkd_app/home.html
@@ -0,0 +1,94 @@
+<!--
+ 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.
+-->
+ 
+{% extends 'base.html' %}
+
+{% block content %}
+    <h1>Apps</h1>
+
+    <div class="row">
+        <div class="col">
+            {{ apps | length }} apps found in context <i>{{ session['context_uuid'] }}</i>
+        </div>
+    </div>
+    
+    <table class="table table-striped table-hover">
+        <thead>
+          <tr>
+            <th scope="col">UUID</th>
+            <th scope="col">Status</th>
+            <th scope="col">Type</th>
+            <th scope="col">Device 1</th>
+            <th scope="col">Device 2</th>
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+            {% if apps %}
+                {% for app in apps %}
+                <tr>
+                    <td>
+                            {{ app.app_id.app_uuid.uuid }}
+                    </td>
+                    <td>
+                        {{ ase.Name(app.app_status).replace('QKDAPPSTATUS_', '') }}
+                    </td>
+                    <td>
+                        {{ ate.Name(app.app_type).replace('QKDAPPTYPES_', '').replace('CLIENT', 'EXTERNAL')  }}
+                    </td>
+                    <td>
+                        <li>
+                            <a href="{{ url_for('device.detail', device_uuid=app.local_device_id.device_uuid.uuid) }}">
+                                {{ device_names.get(app.local_device_id.device_uuid.uuid, app.local_device_id.device_uuid.uuid) }}
+                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                    <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                    <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                                </svg>
+                            </a>
+                        </li>
+                    </td>
+                    <td>
+                        {% if app.remote_device_id.device_uuid.uuid %}   
+                            <li>       
+                                <a href="{{ url_for('device.detail', device_uuid=app.remote_device_id.device_uuid.uuid) }}">
+                                    {{ device_names.get(app.remote_device_id.device_uuid.uuid, app.remote_device_id.device_uuid.uuid) }}
+                                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                        <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                        <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                                    </svg>
+                                </a>
+                            </li>
+                        {% endif %}
+                    </td>
+                    <td>
+                        <a href="{{ url_for('app.detail', app_uuid=app.app_id.app_uuid.uuid) }}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
+                                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                </tr>
+                {% endfor %}
+            {% else %}
+                <tr>
+                    <td colspan="7">No apps found</td>
+                </tr>
+            {% endif %}
+        </tbody>
+    </table>
+
+{% endblock %}
diff --git a/src/webui/service/templates/service/configure_QKD.html b/src/webui/service/templates/service/configure_QKD.html
new file mode 100644
index 0000000000000000000000000000000000000000..4f3da37dab3a47f04d657d8501be8e3d834c4718
--- /dev/null
+++ b/src/webui/service/templates/service/configure_QKD.html
@@ -0,0 +1,188 @@
+<!--
+ Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+-->
+
+{% extends 'base.html' %}
+
+{% block content %}
+<h1>Add New Service [QKD]</h1>
+<form method="POST" action="{{ url_for('service.add_configure_QKD') }}">
+    <fieldset>
+        <div class="row mb-3">
+            {{ form_qkd.hidden_tag() }}
+        </div> 
+        <h3>Generic Service Parameters</h3>
+        {% if form_qkd.qkd_params is not none %}
+        <div class="row mb-3">
+            {{ form_qkd.service_name.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_name.errors %}
+                {{ form_qkd.service_name(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_name.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_name(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_type.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_type.errors %}
+                {{ form_qkd.service_type(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_type.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_type(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_device_1.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-4">
+                {% if form_qkd.service_device_1.errors %}
+                {{ form_qkd.service_device_1(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_device_1.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_device_1(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+            {{ form_qkd.service_device_2.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-4">
+                {% if form_qkd.service_device_2.errors %}
+                {{ form_qkd.service_device_2(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_device_2.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_device_2(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_endpoint_1.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-4">
+                {% if form_qkd.service_endpoint_1.errors %}
+                {{ form_qkd.service_endpoint_1(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_endpoint_1.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_endpoint_1(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+            {{ form_qkd.service_endpoint_2.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-4">
+                {% if form_qkd.service_endpoint_2.errors %}
+                {{ form_qkd.service_endpoint_2(class="form-control is-invalid", placeholder="Mandatory") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_endpoint_2.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_endpoint_2(class="form-control", placeholder="Mandatory") }}
+                {% endif %}
+            </div>
+        </div>
+        </br>
+        <h3>Generic Service Constraints</h3>
+        <div class="row mb-3">
+            {{ form_qkd.service_capacity.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_capacity.errors %}
+                {{ form_qkd.service_capacity(class="form-control is-invalid") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_capacity.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_capacity(class="form-control") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_latency.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_latency.errors %}
+                {{ form_qkd.service_latency(class="form-control is-invalid") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_latency.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_latency(class="form-control") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_availability.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_availability.errors %}
+                {{ form_qkd.service_availability(class="form-control is-invalid") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_availability.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_availability(class="form-control") }}
+                {% endif %}
+            </div>
+        </div>
+        <div class="row mb-3">
+            {{ form_qkd.service_isolation.label(class="col-sm-2 col-form-label") }}
+            <div class="col-sm-10">
+                {% if form_qkd.service_isolation.errors %}
+                {{ form_qkd.service_isolation(class="form-control is-invalid") }}
+                <div class="invalid-feedback">
+                    {% for error in form_qkd.service_isolation.errors %}
+                    <span>{{ error }}</span>
+                    {% endfor %}
+                </div>
+                {% else %}
+                {{ form_qkd.service_isolation(class="form-control") }}
+                {% endif %}
+            </div>
+        </div>
+        {% endif %}
+        <button type="submit" class="btn btn-primary">
+            <i class="bi bi-plus-circle-fill"></i>
+            {{ submit_text }}
+        </button>
+        <button type="button" class="btn btn-block btn-secondary" onclick="javascript: history.back()">
+            <i class="bi bi-box-arrow-in-left"></i>
+            Cancel
+        </button>
+    </fieldset>
+    </form>
+    {% endblock %}
+