diff --git a/proto/qkd_app.proto b/proto/qkd_app.proto
index 7b6c47330833849b889e770aac43844ec6e6072c..b15aaa5a2c533961e7ff9cd79380d62f67db640f 100644
--- a/proto/qkd_app.proto
+++ b/proto/qkd_app.proto
@@ -3,9 +3,10 @@ package qkd_app;
 
 import "context.proto";
 
-// Optare: Change this if you want to change App's structure or enums. 
-// Optare: If a message (structure) is changed it must be changed in src/app/service/database
+// Define Empty message if you don't want to use google.protobuf.Empty.
+message Empty {}
 
+// Enum representing possible states of a QKD application.
 enum QKDAppStatusEnum {
   QKDAPPSTATUS_ON = 0;
   QKDAPPSTATUS_DISCONNECTED = 1;
@@ -13,16 +14,26 @@ enum QKDAppStatusEnum {
   QKDAPPSTATUS_ZOMBIE = 3;
 }
 
+// Enum representing QKD application types.
 enum QKDAppTypesEnum {
   QKDAPPTYPES_INTERNAL = 0;
   QKDAPPTYPES_CLIENT = 1;
 }
 
+// Message representing a QKDL (Quantum Key Distribution Link) identifier.
 message QKDLId {
   context.Uuid qkdl_uuid = 1;
 }
 
+// Define QoS parameters for QKD applications
+message QoS {
+  uint32 max_bandwidth = 1; // Maximum bandwidth (in bits per second)
+  uint32 min_bandwidth = 2; // Minimum bandwidth (optional)
+  uint32 jitter = 3;        // Maximum jitter (in milliseconds)
+  uint32 ttl = 4;           // Time-to-live (in seconds)
+}
 
+// Main message representing a QKD application with all required fields.
 message App {
   AppId app_id = 1;
   QKDAppStatusEnum app_status = 2;
@@ -32,22 +43,24 @@ message App {
   repeated QKDLId backing_qkdl_id = 6;
   context.DeviceId local_device_id = 7;
   context.DeviceId remote_device_id = 8;
+  QoS qos = 9; // Include QoS in the App message
 }
 
-
+// Message representing an identifier for an app.
 message AppId {
   context.ContextId context_id = 1;
   context.Uuid app_uuid = 2;
 }
 
-
+// Service definition for AppService, including app registration and listing.
 service AppService {
   rpc RegisterApp(App) returns (context.Empty) {}
-  rpc ListApps       (context.ContextId     ) returns (       AppList     ) {}
- }
- 
-
+  rpc ListApps(context.ContextId) returns (AppList) {}
+  rpc GetApp(AppId) returns (App) {} 
+  rpc DeleteApp (AppId) returns (Empty) {} // Use locally defined Empty
+}
 
- message AppList {
+// Message representing a list of apps.
+message AppList {
   repeated App apps = 1;
 }
diff --git a/src/qkd_app/client/QKDAppClient.py b/src/qkd_app/client/QKDAppClient.py
index 1a174df6adc69ab9ce88b0d8878c92b9b9e7820e..6ee2012d07ea88aa1eef99d52bc853d37f8b2bf3 100644
--- a/src/qkd_app/client/QKDAppClient.py
+++ b/src/qkd_app/client/QKDAppClient.py
@@ -12,53 +12,96 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import grpc, logging
+import grpc
+import logging
 from common.Constants import ServiceNameEnum
 from common.Settings import get_service_host, get_service_port_grpc
-from common.proto.context_pb2 import Empty, ContextId
 from common.proto.qkd_app_pb2 import App, AppId, AppList
 from common.proto.qkd_app_pb2_grpc import AppServiceStub
 from common.tools.client.RetryDecorator import retry, delay_exponential
 from common.tools.grpc.Tools import grpc_message_to_json_string
 
 LOGGER = logging.getLogger(__name__)
+
+# Define retry mechanism
 MAX_RETRIES = 15
 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
-RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
 
 class QKDAppClient:
     def __init__(self, host=None, port=None):
-        if not host: host = get_service_host(ServiceNameEnum.QKD_APP)
-        if not port: port = get_service_port_grpc(ServiceNameEnum.QKD_APP)
-        self.endpoint = '{:s}:{:s}'.format(str(host), str(port))
-        LOGGER.debug('Creating channel to {:s}...'.format(self.endpoint))
+        self.host = host or get_service_host(ServiceNameEnum.QKD_APP)
+        self.port = port or get_service_port_grpc(ServiceNameEnum.QKD_APP)
+        self.endpoint = f'{self.host}:{self.port}'
+        LOGGER.debug(f'Initializing gRPC client to {self.endpoint}...')
         self.channel = None
         self.stub = None
         self.connect()
-        LOGGER.debug('Channel created')
 
     def connect(self):
-        self.channel = grpc.insecure_channel(self.endpoint)
-        self.stub = AppServiceStub(self.channel)
+        try:
+            self.channel = grpc.insecure_channel(self.endpoint)
+            self.stub = AppServiceStub(self.channel)
+            LOGGER.debug(f'gRPC channel to {self.endpoint} established successfully')
+        except Exception as e:
+            LOGGER.error(f"Failed to establish gRPC connection: {e}")
+            self.stub = None
 
     def close(self):
-        if self.channel is not None: self.channel.close()
+        if self.channel:
+            self.channel.close()
+            LOGGER.debug(f'gRPC channel to {self.endpoint} closed')
         self.channel = None
         self.stub = None
 
+    def check_connection(self):
+        if self.stub is None:
+            LOGGER.error("gRPC connection is not established. Retrying...")
+            self.connect()
+            if self.stub is None:
+                raise ConnectionError("gRPC connection could not be established.")
+
+    @retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION)
+    def RegisterApp(self, app_request: App) -> None:
+        """Register a new QKD app."""
+        self.check_connection()
+        LOGGER.debug(f'RegisterApp request: {grpc_message_to_json_string(app_request)}')
+        self.stub.RegisterApp(app_request)
+        LOGGER.debug('App registered successfully')
 
+    @retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION)
+    def UpdateApp(self, app_request: App) -> None:
+        """Update an existing QKD app."""
+        self.check_connection()
+        LOGGER.debug(f'UpdateApp request: {grpc_message_to_json_string(app_request)}')
+        self.stub.UpdateApp(app_request)
+        LOGGER.debug('App updated successfully')
 
-    @RETRY_DECORATOR
-    def RegisterApp(self, request : App) -> Empty:
-        LOGGER.debug('RegisterApp request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.RegisterApp(request)
-        LOGGER.debug('RegisterApp result: {:s}'.format(grpc_message_to_json_string(response)))
+    @retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION)
+    def ListApps(self, context_id) -> AppList:
+        """List all apps for a given context."""
+        self.check_connection()
+        LOGGER.debug(f'ListApps request for context_id: {grpc_message_to_json_string(context_id)}')
+        response = self.stub.ListApps(context_id)
+        LOGGER.debug(f'ListApps result: {grpc_message_to_json_string(response)}')
         return response
 
-    
-    @RETRY_DECORATOR
-    def ListApps(self, request: ContextId) -> AppList:
-        LOGGER.debug('ListApps request: {:s}'.format(grpc_message_to_json_string(request)))
-        response = self.stub.ListApps(request)
-        LOGGER.debug('ListApps result: {:s}'.format(grpc_message_to_json_string(response)))
+    @retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION)
+    def GetApp(self, app_id: AppId) -> App:
+        """Fetch details of a specific app by its ID."""
+        self.check_connection()
+        LOGGER.debug(f'GetApp request for app_id: {grpc_message_to_json_string(app_id)}')
+        response = self.stub.GetApp(app_id)
+        LOGGER.debug(f'GetApp result: {grpc_message_to_json_string(response)}')
         return response
+
+    @retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION)
+    def DeleteApp(self, app_id: AppId) -> None:
+        """Delete an app by its ID."""
+        self.check_connection()  # Ensures connection is established
+        LOGGER.debug(f'DeleteApp request for app_id: {grpc_message_to_json_string(app_id)}')
+        self.stub.DeleteApp(app_id)  # Calls the gRPC service
+        LOGGER.debug('App deleted successfully')
+
+
+
+
diff --git a/src/qkd_app/service/QKDAppService.py b/src/qkd_app/service/QKDAppService.py
index a6c93cd811a72594804fe8e8e86a9586533a1317..b9b34ed9b8239ea119c5af1364bbc53914b42c51 100644
--- a/src/qkd_app/service/QKDAppService.py
+++ b/src/qkd_app/service/QKDAppService.py
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging, sqlalchemy
+import logging
+import sqlalchemy
 from common.Constants import ServiceNameEnum
 from common.Settings import get_service_port_grpc
 from common.message_broker.MessageBroker import MessageBroker
@@ -20,18 +21,38 @@ from common.proto.qkd_app_pb2_grpc import add_AppServiceServicer_to_server
 from common.tools.service.GenericGrpcService import GenericGrpcService
 from qkd_app.service.QKDAppServiceServicerImpl import AppServiceServicerImpl
 
-# Custom gRPC settings
-GRPC_MAX_WORKERS = 200 # multiple clients might keep connections alive for Get*Events() RPC methods
+# Configure maximum number of workers for gRPC
+GRPC_MAX_WORKERS = 200  # Adjusted for high concurrency
 LOGGER = logging.getLogger(__name__)
 
-
 class AppService(GenericGrpcService):
+    """
+    gRPC Service for handling QKD App-related operations. 
+    This class initializes the gRPC server and installs the servicers.
+    """
     def __init__(
-        self, db_engine : sqlalchemy.engine.Engine, messagebroker : MessageBroker, cls_name: str = __name__
+        self, db_engine: sqlalchemy.engine.Engine, messagebroker: MessageBroker, cls_name: str = __name__
     ) -> None:
+        """
+        Initializes the AppService with the provided database engine and message broker.
+        Sets up the gRPC server to handle app-related requests.
+
+        Args:
+            db_engine (sqlalchemy.engine.Engine): Database engine for handling app data.
+            messagebroker (MessageBroker): Message broker for inter-service communication.
+            cls_name (str): Class name for logging purposes (default is __name__).
+        """
+        # Get the port for the gRPC AppService
         port = get_service_port_grpc(ServiceNameEnum.QKD_APP)
+        # Initialize the base class with port and max worker configuration
         super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name)
+        # Initialize the AppServiceServicer with the database and message broker
         self.app_servicer = AppServiceServicerImpl(db_engine, messagebroker)
 
     def install_servicers(self):
+        """
+        Installs the AppService servicers to the gRPC server.
+        This allows the server to handle requests for QKD app operations.
+        """
         add_AppServiceServicer_to_server(self.app_servicer, self.server)
+        LOGGER.debug("AppService servicer installed")
diff --git a/src/qkd_app/service/QKDAppServiceServicerImpl.py b/src/qkd_app/service/QKDAppServiceServicerImpl.py
index df7a885c47eda9d7a6137c9905388da49c698e7e..3ef13b75e5510a95b91a9a67fa65ed06c32a3526 100644
--- a/src/qkd_app/service/QKDAppServiceServicerImpl.py
+++ b/src/qkd_app/service/QKDAppServiceServicerImpl.py
@@ -12,62 +12,175 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import grpc, logging, sqlalchemy
-#from typing import Iterator, Optional
+import grpc
+import logging
+import sqlalchemy
+import uuid
 from common.message_broker.MessageBroker import MessageBroker
-import grpc, json, logging #, deepdiff
-from common.proto.context_pb2 import (
-    Empty, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, ContextId)
-from common.proto.qkd_app_pb2 import (App, AppId, AppList, QKDAppTypesEnum)
+from common.proto.context_pb2 import Empty, ContextId
+from common.proto.qkd_app_pb2 import App, AppId, AppList, QKDAppTypesEnum, QoS
+from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException
 from common.proto.qkd_app_pb2_grpc import AppServiceServicer
+from common.tools.grpc.Tools import grpc_message_to_json_string
 from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method
-#from common.tools.context_queries.InterDomain import is_inter_domain #, is_multi_domain
-#from common.tools.grpc.ConfigRules import copy_config_rules
-#from common.tools.grpc.Constraints import copy_constraints
-#from common.tools.grpc.EndPointIds import copy_endpoint_ids
-#from common.tools.grpc.ServiceIds import update_service_ids
-#from common.tools.grpc.Tools import grpc_message_to_json_string
-#from context.client.ContextClient import ContextClient
-#from qkd_app.client.QKDAppClient import QKDAppClient
-from .database.QKDApp import app_set, app_list_objs, app_get, app_get_by_server
-from common.method_wrappers.ServiceExceptions import NotFoundException
+from .database.QKDApp import app_set, app_list_objs, app_get, app_get_by_server, app_delete
 
 LOGGER = logging.getLogger(__name__)
 
 METRICS_POOL = MetricsPool('QkdApp', 'RPC')
 
-# Optare: This file must be edited based on app's logic
-
 class AppServiceServicerImpl(AppServiceServicer):
-    def __init__(self, db_engine : sqlalchemy.engine.Engine, messagebroker : MessageBroker):
-        LOGGER.debug('Creating Servicer...')
+    def __init__(self, db_engine: sqlalchemy.engine.Engine, messagebroker: MessageBroker):
+        LOGGER.debug('Initializing AppServiceServicer...')
         self.db_engine = db_engine
         self.messagebroker = messagebroker
-        LOGGER.debug('Servicer Created')
+        LOGGER.debug('AppServiceServicer initialized')
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def RegisterApp(self, request : App, context : grpc.ServicerContext) -> Empty:
-        # Optare: This is the main function required for the project.
-        # Optare: If it's an internal it will save it directly. If it's an external one it will save it as pending by not providing the remote until the other party requests it too
-        # Optare: Ideally, the only thing needed to change is the code inside the try block. Currently it just searches by a pending app with the same server_id but you can put more restrictions or different search and raise the NotFoundException
+    def RegisterApp(self, request: App, context: grpc.ServicerContext) -> Empty:
+        """
+        Registers an app in the system, handling both internal and external applications
+        with ETSI GS QKD 015 compliance.
+        """
+        LOGGER.debug(f"Received RegisterApp request: {grpc_message_to_json_string(request)}")
 
-        if request.app_type == QKDAppTypesEnum.QKDAPPTYPES_INTERNAL:
-            app_set(self.db_engine, self.messagebroker, request)
+        try:
+            # Validate QoS parameters as per ETSI 015 requirements
+            self._validate_qos(request.qos)
 
-        else:
-            try:
-                app = app_get_by_server(self.db_engine, request.server_app_id)
-            except NotFoundException:
-                app = request
-                app_set(self.db_engine, self.messagebroker, app)
+            # Check if an app with the same server_app_id and local_device_id already exists
+            existing_app = self._check_existing_app(request.server_app_id, request.local_device_id.device_uuid.uuid)
+
+            if existing_app:
+                if request.app_type == QKDAppTypesEnum.QKDAPPTYPES_CLIENT:
+                    LOGGER.debug(f"Handling external app registration for server_app_id: {request.server_app_id}")
+                    # Handle second-party registration for external apps
+                    if not existing_app.remote_device_id.device_uuid.uuid:
+                        existing_app.remote_device_id.device_uuid.uuid = request.local_device_id.device_uuid.uuid
+                        app_set(self.db_engine, self.messagebroker, existing_app)
+                        LOGGER.debug(f"Updated external app with server_app_id: {request.server_app_id}, remote_device_id: {request.local_device_id.device_uuid.uuid}")
+                    else:
+                        context.set_code(grpc.StatusCode.ALREADY_EXISTS)
+                        context.set_details(f"App with server_app_id {request.server_app_id} already has both parties registered.")
+                        return Empty()
+                else:
+                    context.set_code(grpc.StatusCode.ALREADY_EXISTS)
+                    context.set_details(f"App with server_app_id {request.server_app_id} already exists.")
+                    return Empty()
             else:
-                app.remote_device_id.device_uuid.uuid = request.local_device_id.device_uuid.uuid
-                app_set(self.db_engine, self.messagebroker, app)
-                
-        
-        return Empty()
-    
+                # Assign application IDs as required
+                self._validate_and_assign_app_ids(request)
+
+                # Register the app
+                if request.app_type == QKDAppTypesEnum.QKDAPPTYPES_INTERNAL:
+                    LOGGER.debug(f"Registering internal app with app_uuid: {request.app_id.app_uuid.uuid}")
+                    app_set(self.db_engine, self.messagebroker, request)
+                else:
+                    self._register_external_app(request)
+
+            LOGGER.debug(f"RegisterApp completed successfully for app: {request.server_app_id}")
+            return Empty()
+
+        except InvalidArgumentException as e:
+            context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+            context.set_details(str(e))
+            raise e
+        except Exception as e:
+            context.set_code(grpc.StatusCode.INTERNAL)
+            context.set_details("An internal error occurred during app registration.")
+            raise e
+
+    def _validate_qos(self, qos: QoS) -> None:
+        """
+        Validates the QoS parameters for the application, ensuring ETSI 015 compliance.
+        """
+        if qos.max_bandwidth and qos.min_bandwidth and qos.max_bandwidth < qos.min_bandwidth:
+            raise InvalidArgumentException("QoS max_bandwidth cannot be less than min_bandwidth.")
+
+        if qos.ttl and qos.ttl <= 0:
+            raise InvalidArgumentException("QoS TTL must be a positive value.")
+
+        LOGGER.debug(f"QoS validated: {qos}")
+
+    def _check_existing_app(self, server_app_id: str, local_device_id: str):
+        try:
+            return app_get_by_server(self.db_engine, server_app_id)
+        except NotFoundException:
+            return None
+
+    def _validate_and_assign_app_ids(self, request: App) -> None:
+        """
+        Validates and assigns app IDs (app_uuid, server_app_id, client_app_id) if not provided.
+        """
+        if not request.app_id.app_uuid.uuid:
+            request.app_id.app_uuid.uuid = str(uuid.uuid4())
+            LOGGER.debug(f"Assigned new app_uuid: {request.app_id.app_uuid.uuid}")
+
+        if not request.server_app_id:
+            request.server_app_id = str(uuid.uuid4())
+            LOGGER.debug(f"Assigned new server_app_id: {request.server_app_id}")
+
+        del request.client_app_id[:]  # Clear the repeated field for clients
+
+    def _register_external_app(self, request: App) -> None:
+        try:
+            existing_app = app_get_by_server(self.db_engine, request.server_app_id)
+
+            if not existing_app.remote_device_id.device_uuid.uuid:
+                existing_app.remote_device_id.device_uuid.uuid = request.local_device_id.device_uuid.uuid
+                app_set(self.db_engine, self.messagebroker, existing_app)
+            else:
+                LOGGER.debug(f"App with server_app_id: {request.server_app_id} already has both parties registered.")
+        except NotFoundException:
+            app_set(self.db_engine, self.messagebroker, request)
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def ListApps(self, request: ContextId, context: grpc.ServicerContext) -> AppList:
+        """
+        Lists all apps in the system, including their statistics and QoS attributes.
+        """
+        LOGGER.debug(f"Received ListApps request: {grpc_message_to_json_string(request)}")
+
+        try:
+            apps = app_list_objs(self.db_engine, request.context_uuid.uuid)
+            for app in apps.apps:
+                LOGGER.debug(f"App retrieved: {grpc_message_to_json_string(app)}")
+
+            LOGGER.debug(f"ListApps returned {len(apps.apps)} apps for context_id: {request.context_uuid.uuid}")
+            return apps
+        except Exception as e:
+            context.set_code(grpc.StatusCode.INTERNAL)
+            context.set_details("An internal error occurred while listing apps.")
+            raise e
+
+    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
+    def GetApp(self, request: AppId, context: grpc.ServicerContext) -> App:
+        """
+        Fetches details of a specific app based on its AppId, including QoS and performance stats.
+        """
+        LOGGER.debug(f"Received GetApp request: {grpc_message_to_json_string(request)}")
+        try:
+            app = app_get(self.db_engine, request)
+            LOGGER.debug(f"GetApp found app with app_uuid: {request.app_uuid.uuid}")
+            return app
+        except NotFoundException as e:
+            context.set_code(grpc.StatusCode.NOT_FOUND)
+            context.set_details(f"App not found: {e}")
+            raise e
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def ListApps(self, request: ContextId, context : grpc.ServicerContext) -> AppList:
-        return app_list_objs(self.db_engine)
+    def DeleteApp(self, request: AppId, context: grpc.ServicerContext) -> Empty:
+        """
+        Deletes an app from the system by its AppId, following ETSI compliance.
+        """
+        LOGGER.debug(f"Received DeleteApp request for app_uuid: {request.app_uuid.uuid}")
+        try:
+            app_delete(self.db_engine, request.app_uuid.uuid)
+            LOGGER.debug(f"App with UUID {request.app_uuid.uuid} deleted successfully.")
+            return Empty()
+        except NotFoundException as e:
+            context.set_code(grpc.StatusCode.NOT_FOUND)
+            context.set_details(f"App not found: {e}")
+            raise e
+
+
diff --git a/src/qkd_app/service/__main__.py b/src/qkd_app/service/__main__.py
index ed7e554728eb2de6240dd4facb7f084337a026a4..ed9bd011e1ae7bdd69165a2ed66ba46d97356130 100644
--- a/src/qkd_app/service/__main__.py
+++ b/src/qkd_app/service/__main__.py
@@ -12,7 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging, signal, sys, threading
+import logging
+import signal
+import sys
+import threading
 from prometheus_client import start_http_server
 #from common.Constants import ServiceNameEnum
 from common.Settings import (
@@ -26,68 +29,85 @@ from qkd_app.service.rest_server.qkd_app import register_qkd_app
 from qkd_app.service.database.Engine import Engine
 from qkd_app.service.database.models._Base import rebuild_database
 
+# Event for terminating the service gracefully
 terminate = threading.Event()
-LOGGER : logging.Logger = None
+LOGGER: logging.Logger = None
 
-def signal_handler(signal, frame): # pylint: disable=redefined-outer-name
-    LOGGER.warning('Terminate signal received')
+def signal_handler(signum, frame):
+    """
+    Handle termination signals like SIGINT and SIGTERM to ensure graceful shutdown.
+    """
+    LOGGER.warning('Termination signal received')
     terminate.set()
 
 def main():
-    global LOGGER # pylint: disable=global-statement
+    global LOGGER  # Required due to global scope
 
+    # Set up logging
     log_level = get_log_level()
     logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
     LOGGER = logging.getLogger(__name__)
 
+    # Ensure necessary environment variables are set
     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),
     ])
 
-    signal.signal(signal.SIGINT,  signal_handler)
+    # Register signal handlers for graceful shutdown
+    signal.signal(signal.SIGINT, signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
 
     LOGGER.info('Starting...')
 
-    # Start metrics server
+    # Start Prometheus metrics server
     metrics_port = get_metrics_port()
     start_http_server(metrics_port)
+    LOGGER.info(f'Metrics server started on port {metrics_port}')
 
-    # Get Database Engine instance and initialize database, if needed
+    # Initialize the SQLAlchemy database engine
     LOGGER.info('Getting SQLAlchemy DB Engine...')
     db_engine = Engine.get_engine()
     if db_engine is None:
-        LOGGER.error('Unable to get SQLAlchemy DB Engine...')
+        LOGGER.error('Unable to get SQLAlchemy DB Engine. Exiting...')
         return -1
 
+    # Try creating the database or log any issues
     try:
         Engine.create_database(db_engine)
-    except: # pylint: disable=bare-except # pragma: no cover
-        LOGGER.exception('Failed to check/create the database: {:s}'.format(str(db_engine.url)))
+    except Exception as e:  # More specific exception handling
+        LOGGER.exception(f'Failed to check/create the database: {db_engine.url}. Error: {str(e)}')
+        return -1
 
+    # Rebuild the database schema if necessary
     rebuild_database(db_engine)
 
-    # Get message broker instance
-    messagebroker = None #MessageBroker(get_messagebroker_backend())
+    # Initialize the message broker (if needed)
+    messagebroker = None  # Disabled until further notice, can be re-enabled when necessary
+    # messagebroker = MessageBroker(get_messagebroker_backend())
 
-    # Starting context service
+    # Start the gRPC App Service
     grpc_service = AppService(db_engine, messagebroker)
     grpc_service.start()
 
+    # Start the REST server and register QKD apps
     rest_server = RestServer()
     register_qkd_app(rest_server)
     rest_server.start()
 
-    # Wait for Ctrl+C or termination signal
-    while not terminate.wait(timeout=1.0): pass
+    LOGGER.info('Services started. Waiting for termination signal...')
+
+    # Keep the process running until a termination signal is received
+    while not terminate.wait(timeout=1.0):
+        pass
 
-    LOGGER.info('Terminating...')
+    # Shutdown services gracefully on termination
+    LOGGER.info('Terminating services...')
     grpc_service.stop()
     rest_server.shutdown()
     rest_server.join()
 
-    LOGGER.info('Bye')
+    LOGGER.info('Shutdown complete. Exiting...')
     return 0
 
 if __name__ == '__main__':
diff --git a/src/qkd_app/service/database/QKDApp.py b/src/qkd_app/service/database/QKDApp.py
index b1fb90d4efcd0770bcc4c48c1f00deb0e95687ad..539555a4dac055d56158298ed7db6dceec39f446 100644
--- a/src/qkd_app/service/database/QKDApp.py
+++ b/src/qkd_app/service/database/QKDApp.py
@@ -12,174 +12,162 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import datetime, logging, uuid
+import datetime
+import logging
+from typing import Dict, List, Optional
+
 from sqlalchemy.dialects.postgresql import insert
 from sqlalchemy.engine import Engine
-from sqlalchemy.orm import Session, selectinload, sessionmaker
+from sqlalchemy.orm import Session, sessionmaker
 from sqlalchemy_cockroachdb import run_transaction
-from typing import Dict, List, Optional, Set, Tuple
-from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException
+
+from common.method_wrappers.ServiceExceptions import NotFoundException
 from common.message_broker.MessageBroker import MessageBroker
-from common.proto.context_pb2 import Empty
-from common.proto.qkd_app_pb2 import (
-    AppList, App, AppId)
-from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.proto.qkd_app_pb2 import AppList, App, AppId
+from qkd_app.service.database.uuids._Builder import get_uuid_from_string, get_uuid_random
+from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
+from common.tools.object_factory.QKDApp import json_app_id
+from common.tools.object_factory.Context import json_context_id
+
 from .models.QKDAppModel import AppModel
-from .models.enums.QKDAppStatus import grpc_to_enum__qkd_app_status
-from .models.enums.QKDAppTypes import grpc_to_enum__qkd_app_types
 from .uuids.QKDApp import app_get_uuid
-from common.tools.object_factory.Context import json_context_id
-from common.tools.object_factory.QKDApp import json_app_id
 from context.service.database.uuids.Context import context_get_uuid
+from .models.enums.QKDAppStatus import grpc_to_enum__qkd_app_status
+from .models.enums.QKDAppTypes import grpc_to_enum__qkd_app_types
 
+LOGGER = logging.getLogger(__name__)
 
 
-#from .Events import notify_event_context, notify_event_device, notify_event_topology
+def app_list_objs(db_engine: Engine, context_uuid: str = None) -> AppList:
+    """
+    Fetches a list of all QKD applications from the database. Optionally filters by context UUID.
 
-LOGGER = logging.getLogger(__name__)
+    :param db_engine: SQLAlchemy Engine for DB connection
+    :param context_uuid: UUID of the context to filter by (optional)
+    :return: AppList containing all apps
+    """
+    def callback(session: Session) -> List[Dict]:
+        query = session.query(AppModel)
+        
+        if context_uuid:
+            query = query.filter_by(context_uuid=context_uuid)
 
+        return [obj.dump() for obj in query.all()]
 
-def app_list_objs(db_engine : Engine) -> AppList:
-    def callback(session : Session) -> List[Dict]:
-        obj_list : List[AppModel] = session.query(AppModel)\
-            .all()
-        return [obj.dump() for obj in obj_list]
     apps = run_transaction(sessionmaker(bind=db_engine), callback)
     return AppList(apps=apps)
 
-def app_get(db_engine : Engine, request : AppId) -> App:
+
+def app_get(db_engine: Engine, request: AppId) -> App:
+    """
+    Fetches a specific app by its UUID.
+
+    :param db_engine: SQLAlchemy Engine for DB connection
+    :param request: AppId protobuf containing app ID and context ID
+    :return: App protobuf object
+    :raises NotFoundException: If the app is not found in the database
+    """
     app_uuid = app_get_uuid(request, allow_random=False)
-    def callback(session : Session) -> Optional[Dict]:
-        obj : Optional[AppModel] = session.query(AppModel)\
-            .filter_by(app_uuid=app_uuid).one_or_none()
-        return None if obj is None else obj.dump()
+
+    def callback(session: Session) -> Optional[Dict]:
+        obj = session.query(AppModel).filter_by(app_uuid=app_uuid).one_or_none()
+        return obj.dump() if obj else None
+
     obj = run_transaction(sessionmaker(bind=db_engine), callback)
-    if obj is None:
-        raw_app_uuid = request.app_uuid.uuid
-        raise NotFoundException('App', raw_app_uuid, extra_details=[
-            'app_uuid generated was: {:s}'.format(app_uuid)
+    
+    if not obj:
+        raise NotFoundException('App', request.app_uuid.uuid, extra_details=[
+            f'app_uuid generated was: {app_uuid}'
         ])
+    
     return App(**obj)
 
-def app_set(db_engine : Engine, messagebroker : MessageBroker, request : App) -> AppId:
-    context_uuid = context_get_uuid(request.app_id.context_id, allow_random=False)
-    raw_app_uuid = request.app_id.app_uuid.uuid
-    app_uuid = app_get_uuid(request.app_id, allow_random=True)
 
-    app_type = request.app_type
-    app_status = grpc_to_enum__qkd_app_status(request.app_status)
-    app_type = grpc_to_enum__qkd_app_types(request.app_type)
+def app_set(db_engine: Engine, messagebroker: MessageBroker, request: App) -> AppId:
+    """
+    Creates or updates an app in the database. If the app already exists, updates the app.
+    Otherwise, inserts a new entry.
 
-    now = datetime.datetime.utcnow()
+    :param db_engine: SQLAlchemy Engine for DB connection
+    :param messagebroker: MessageBroker instance for notifications
+    :param request: App protobuf object containing app data
+    :return: AppId protobuf object representing the newly created or updated app
+    """
+    context_uuid = context_get_uuid(request.app_id.context_id, allow_random=False)
+    app_uuid = app_get_uuid(request.app_id, allow_random=True)
 
-    
-    app_data = [{
-        'context_uuid'       : context_uuid,
-        'app_uuid'           : app_uuid,
-        'app_status'         : app_status,
-        'app_type'           : app_type,
-        'server_app_id'      : request.server_app_id,
-        'client_app_id'      : request.client_app_id,
-        'backing_qkdl_uuid'  : [qkdl_id.qkdl_uuid.uuid for qkdl_id in request.backing_qkdl_id],
-        'local_device_uuid'  : request.local_device_id.device_uuid.uuid,
-        'remote_device_uuid' : request.remote_device_id.device_uuid.uuid or None,
-        'created_at'         : now,
-        'updated_at'         : now,
-    }]
-
-
-    def callback(session : Session) -> Tuple[bool, List[Dict]]:
+    # Prepare app data for insertion/update
+    app_data = {
+        'context_uuid': context_uuid,
+        'app_uuid': app_uuid,
+        'app_status': grpc_to_enum__qkd_app_status(request.app_status),
+        'app_type': grpc_to_enum__qkd_app_types(request.app_type),
+        'server_app_id': request.server_app_id,
+        'client_app_id': request.client_app_id,
+        'backing_qkdl_uuid': [qkdl.qkdl_uuid.uuid for qkdl in request.backing_qkdl_id],
+        'local_device_uuid': request.local_device_id.device_uuid.uuid,
+        'remote_device_uuid': request.remote_device_id.device_uuid.uuid if request.remote_device_id.device_uuid.uuid else None,
+        'created_at': datetime.datetime.utcnow(),
+        'updated_at': datetime.datetime.utcnow(),
+    }
+
+    def callback(session: Session) -> bool:
+        # Create the insert statement
         stmt = insert(AppModel).values(app_data)
+
+        # Apply the conflict resolution
         stmt = stmt.on_conflict_do_update(
             index_elements=[AppModel.app_uuid],
             set_=dict(
-                app_status         = stmt.excluded.app_status,
-                app_type           = stmt.excluded.app_type,
-                server_app_id      = stmt.excluded.server_app_id,
-                client_app_id      = stmt.excluded.client_app_id,
-                backing_qkdl_uuid  = stmt.excluded.backing_qkdl_uuid,
-                local_device_uuid  = stmt.excluded.local_device_uuid,
-                remote_device_uuid = stmt.excluded.remote_device_uuid,
-                updated_at         = stmt.excluded.updated_at,
+                app_status=stmt.excluded.app_status,
+                app_type=stmt.excluded.app_type,
+                server_app_id=stmt.excluded.server_app_id,
+                client_app_id=stmt.excluded.client_app_id,
+                backing_qkdl_uuid=stmt.excluded.backing_qkdl_uuid,
+                local_device_uuid=stmt.excluded.local_device_uuid,
+                remote_device_uuid=stmt.excluded.remote_device_uuid,
+                updated_at=stmt.excluded.updated_at
             )
         )
-        stmt = stmt.returning(AppModel.created_at, AppModel.updated_at)
-        created_at,updated_at = session.execute(stmt).fetchone()
-        updated = updated_at > created_at
-
-        return updated
-
-    updated = run_transaction(sessionmaker(bind=db_engine), callback)
-    context_id = json_context_id(context_uuid)
-    app_id = json_app_id(app_uuid, context_id=context_id)
-    #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
-    #notify_event_app(messagebroker, event_type, app_id)
-    #notify_event_context(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, context_id)
+        session.execute(stmt)
+        return True
+
+    run_transaction(sessionmaker(bind=db_engine), callback)
+    app_id = json_app_id(app_uuid, context_id=json_context_id(context_uuid))
+
     return AppId(**app_id)
 
 
+def app_get_by_server(db_engine: Engine, server_app_id: str) -> App:
+    """
+    Fetches an app by its server_app_id.
+    """
+    def callback(session: Session) -> Optional[Dict]:
+        obj = session.query(AppModel).filter_by(server_app_id=server_app_id).one_or_none()
+        return obj.dump() if obj else None
 
-def app_get_by_server(db_engine : Engine, request : str) -> App:
-    def callback(session : Session) -> Optional[Dict]:
-        obj : Optional[AppModel] = session.query(AppModel)\
-            .filter_by(server_app_id=request).one_or_none()
-        return None if obj is None else obj.dump()
     obj = run_transaction(sessionmaker(bind=db_engine), callback)
-    if obj is None:
-        raise NotFoundException('No app match found for', request)
+
+    if not obj:
+        raise NotFoundException('App', server_app_id)
+
     return App(**obj)
 
 
+def app_delete(db_engine: Engine, app_uuid: str) -> None:
+    """
+    Deletes an app by its UUID from the database.
+
+    :param db_engine: SQLAlchemy Engine for DB connection
+    :param app_uuid: The UUID of the app to be deleted
+    """
+    def callback(session: Session) -> bool:
+        app_obj = session.query(AppModel).filter_by(app_uuid=app_uuid).one_or_none()
+
+        if app_obj is None:
+            raise NotFoundException('App', app_uuid)
+
+        session.delete(app_obj)
+        return True
 
-"""
-def device_delete(db_engine : Engine, messagebroker : MessageBroker, request : DeviceId) -> Empty:
-    device_uuid = device_get_uuid(request, allow_random=False)
-    def callback(session : Session) -> Tuple[bool, List[Dict]]:
-        query = session.query(TopologyDeviceModel)
-        query = query.filter_by(device_uuid=device_uuid)
-        topology_device_list : List[TopologyDeviceModel] = query.all()
-        topology_ids = [obj.topology.dump_id() for obj in topology_device_list]
-        num_deleted = session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete()
-        return num_deleted > 0, topology_ids
-    deleted, updated_topology_ids = run_transaction(sessionmaker(bind=db_engine), callback)
-    device_id = json_device_id(device_uuid)
-    if deleted:
-        notify_event_device(messagebroker, EventTypeEnum.EVENTTYPE_REMOVE, device_id)
-
-        context_ids  : Dict[str, Dict] = dict()
-        topology_ids : Dict[str, Dict] = dict()
-        for topology_id in updated_topology_ids:
-            topology_uuid = topology_id['topology_uuid']['uuid']
-            topology_ids[topology_uuid] = topology_id
-            context_id = topology_id['context_id']
-            context_uuid = context_id['context_uuid']['uuid']
-            context_ids[context_uuid] = context_id
-
-        for topology_id in topology_ids.values():
-            notify_event_topology(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, topology_id)
-
-        for context_id in context_ids.values():
-            notify_event_context(messagebroker, EventTypeEnum.EVENTTYPE_UPDATE, context_id)
-
-    return Empty()
-
-def device_select(db_engine : Engine, request : DeviceFilter) -> DeviceList:
-    device_uuids = [
-        device_get_uuid(device_id, allow_random=False)
-        for device_id in request.device_ids.device_ids
-    ]
-    dump_params = dict(
-        include_endpoints   =request.include_endpoints,
-        include_config_rules=request.include_config_rules,
-        include_components  =request.include_components,
-    )
-    def callback(session : Session) -> List[Dict]:
-        query = session.query(DeviceModel)
-        if request.include_endpoints   : query = query.options(selectinload(DeviceModel.endpoints))
-        if request.include_config_rules: query = query.options(selectinload(DeviceModel.config_rules))
-        #if request.include_components  : query = query.options(selectinload(DeviceModel.components))
-        obj_list : List[DeviceModel] = query.filter(DeviceModel.device_uuid.in_(device_uuids)).all()
-        return [obj.dump(**dump_params) for obj in obj_list]
-    devices = run_transaction(sessionmaker(bind=db_engine), callback)
-    return DeviceList(devices=devices)
-"""
+    run_transaction(sessionmaker(bind=db_engine), callback)
diff --git a/src/qkd_app/service/database/models/QKDAppModel.py b/src/qkd_app/service/database/models/QKDAppModel.py
index c32b4e28c95105d8659cb52790f51b330764c2cf..d9797d73c9c168efee5810014a70e0425f67e051 100644
--- a/src/qkd_app/service/database/models/QKDAppModel.py
+++ b/src/qkd_app/service/database/models/QKDAppModel.py
@@ -12,52 +12,70 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import operator
-from sqlalchemy import CheckConstraint, Column, DateTime, Float, Enum, ForeignKey, Integer, String
-from sqlalchemy.dialects.postgresql import UUID, ARRAY
-from sqlalchemy.orm import relationship
+from sqlalchemy import Column, DateTime, String, Enum, ARRAY
+from sqlalchemy.dialects.postgresql import UUID
 from typing import Dict
 from ._Base import _Base
 from .enums.QKDAppStatus import ORM_QKDAppStatusEnum
 from .enums.QKDAppTypes import ORM_QKDAppTypesEnum
 
+
 class AppModel(_Base):
+    """
+    ORM model representing a QKD (Quantum Key Distribution) Application.
+    This model stores information about the QKD app status, type, and related device and 
+    backing QKD links. It is stored in the 'qkd_app' table.
+    """
     __tablename__ = 'qkd_app'
 
-    app_uuid            = Column(UUID(as_uuid=False), primary_key=True)
-    context_uuid        = Column(UUID(as_uuid=False), nullable=False) # Supposed to be Foreign Key
-    app_status          = Column(Enum(ORM_QKDAppStatusEnum), nullable=False)
-    app_type            = Column(Enum(ORM_QKDAppTypesEnum), nullable=False)
-    server_app_id       = Column(String, nullable=False)
-    client_app_id       = Column(ARRAY(String), nullable=False)
-    backing_qkdl_uuid   = Column(ARRAY(UUID(as_uuid=False)), nullable=False)
-    local_device_uuid   = Column(UUID(as_uuid=False), nullable=False)
-    remote_device_uuid  = Column(UUID(as_uuid=False), nullable=True)
+    # Primary Key
+    app_uuid = Column(UUID(as_uuid=False), primary_key=True, nullable=False, doc="Unique identifier for the QKD app.")
+    
+    # Foreign Key-like field (context)
+    context_uuid = Column(UUID(as_uuid=False), nullable=False, doc="Foreign key linking to the application's context.")
+
+    # Status and type
+    app_status = Column(Enum(ORM_QKDAppStatusEnum), nullable=False, doc="Current status of the QKD app.")
+    app_type = Column(Enum(ORM_QKDAppTypesEnum), nullable=False, doc="Type of the QKD app (internal or client).")
 
-    # Optare: Created_at and Updated_at are only used to know if an app was updated later on the code. Don't change it
+    # Application IDs
+    server_app_id = Column(String, nullable=False, doc="ID of the server-side QKD application.")
+    client_app_id = Column(ARRAY(String), nullable=False, doc="List of client-side QKD application IDs.")
 
-    created_at          = Column(DateTime, nullable=False)
-    updated_at          = Column(DateTime, nullable=False)
+    # Backing QKD links and devices
+    backing_qkdl_uuid = Column(ARRAY(UUID(as_uuid=False)), nullable=False, doc="List of UUIDs of the backing QKD links.")
+    local_device_uuid = Column(UUID(as_uuid=False), nullable=False, doc="UUID of the local QKD device.")
+    remote_device_uuid = Column(UUID(as_uuid=False), nullable=True, doc="UUID of the remote QKD device (nullable).")
 
-    #__table_args__ = (
-    #    CheckConstraint(... >= 0, name='name_value_...'),
-    #)
+    # Timestamps
+    created_at = Column(DateTime, nullable=False, doc="Timestamp when the QKD app record was created.")
+    updated_at = Column(DateTime, nullable=False, doc="Timestamp when the QKD app record was last updated.")
 
     def dump_id(self) -> Dict:
+        """
+        Serializes the primary key fields (context and app UUID) into a dictionary.
+
+        :return: A dictionary with 'context_id' and 'app_uuid' keys.
+        """
         return {
             'context_id': {'context_uuid': {'uuid': self.context_uuid}},
             'app_uuid': {'uuid': self.app_uuid}
         }
 
     def dump(self) -> Dict:
-        result = {
-            'app_id'           : self.dump_id(),
-            'app_status'       : self.app_status.value,
-            'app_type'         : self.app_type.value,
-            'server_app_id'    : self.server_app_id,
-            'client_app_id'    : self.client_app_id,
-            'backing_qkdl_id'  : [{'qkdl_uuid': {'uuid': qkdl_id}} for qkdl_id in self.backing_qkdl_uuid],
-            'local_device_id'  : {'device_uuid': {'uuid': self.local_device_uuid}},
-            'remote_device_id' : {'device_uuid': {'uuid': self.remote_device_uuid}},
+        """
+        Serializes the entire QKD app model into a dictionary, including app status, type, IDs, 
+        device info, and backing QKD links.
+
+        :return: A dictionary representation of the QKD app.
+        """
+        return {
+            'app_id': self.dump_id(),
+            'app_status': self.app_status.value,
+            'app_type': self.app_type.value,
+            'server_app_id': self.server_app_id,
+            'client_app_id': self.client_app_id,
+            'backing_qkdl_id': [{'qkdl_uuid': {'uuid': qkdl_id}} for qkdl_id in self.backing_qkdl_uuid],
+            'local_device_id': {'device_uuid': {'uuid': self.local_device_uuid}},
+            'remote_device_id': {'device_uuid': {'uuid': self.remote_device_uuid}} if self.remote_device_uuid else None,
         }
-        return result
diff --git a/src/qkd_app/service/database/models/_Base.py b/src/qkd_app/service/database/models/_Base.py
index 51863e1d5c06a875c298eab726cfdc3b7fcb75ca..f17fb9a56dcd120ad1dc95ceee720aa942f6ae6c 100644
--- a/src/qkd_app/service/database/models/_Base.py
+++ b/src/qkd_app/service/database/models/_Base.py
@@ -13,32 +13,17 @@
 # limitations under the License.
 
 import sqlalchemy
-from typing import Any, List
-from sqlalchemy.orm import Session, sessionmaker, declarative_base
-from sqlalchemy.sql import text
-from sqlalchemy_cockroachdb import run_transaction
+from sqlalchemy.orm import declarative_base
 
 _Base = declarative_base()
 
-'''
-def create_performance_enhancers(db_engine : sqlalchemy.engine.Engine) -> None:
-    def index_storing(
-        index_name : str, table_name : str, index_fields : List[str], storing_fields : List[str]
-    ) -> Any:
-        str_index_fields = ','.join(['"{:s}"'.format(index_field) for index_field in index_fields])
-        str_storing_fields = ','.join(['"{:s}"'.format(storing_field) for storing_field in storing_fields])
-        INDEX_STORING = 'CREATE INDEX IF NOT EXISTS {:s} ON "{:s}" ({:s}) STORING ({:s});'
-        return text(INDEX_STORING.format(index_name, table_name, str_index_fields, str_storing_fields))
-
-    statements = [
-        # In case of relations
-    ]
-    def callback(session : Session) -> bool:
-        for stmt in statements: session.execute(stmt)
-    run_transaction(sessionmaker(bind=db_engine), callback)
-'''
-
-def rebuild_database(db_engine : sqlalchemy.engine.Engine, drop_if_exists : bool = False):
-    if drop_if_exists: _Base.metadata.drop_all(db_engine)
+def rebuild_database(db_engine: sqlalchemy.engine.Engine, drop_if_exists: bool = False):
+    """
+    Rebuild the database schema for the QKD application. Optionally drop the existing schema if specified.
+    
+    :param db_engine: SQLAlchemy engine instance.
+    :param drop_if_exists: Boolean indicating if the schema should be dropped before rebuilding.
+    """
+    if drop_if_exists:
+        _Base.metadata.drop_all(db_engine)
     _Base.metadata.create_all(db_engine)
-    #create_performance_enhancers(db_engine)
diff --git a/src/qkd_app/service/database/models/enums/QKDAppStatus.py b/src/qkd_app/service/database/models/enums/QKDAppStatus.py
index d3063ef56704ce1bdd48d15ea8c6486ed7c8cfae..980a8b14adca8c69eaf567037dc09872f33622f9 100644
--- a/src/qkd_app/service/database/models/enums/QKDAppStatus.py
+++ b/src/qkd_app/service/database/models/enums/QKDAppStatus.py
@@ -12,16 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import enum, functools
+import enum
+import functools
 from common.proto.qkd_app_pb2 import QKDAppStatusEnum
 from ._GrpcToEnum import grpc_to_enum
 
+# Enum mapping for ORM-based app statuses.
 class ORM_QKDAppStatusEnum(enum.Enum):
-    ON           = QKDAppStatusEnum.QKDAPPSTATUS_ON
+    ON = QKDAppStatusEnum.QKDAPPSTATUS_ON
     DISCONNECTED = QKDAppStatusEnum.QKDAPPSTATUS_DISCONNECTED
-    OUT_OF_TIME  = QKDAppStatusEnum.QKDAPPSTATUS_OUT_OF_TIME
-    ZOMBIE       = QKDAppStatusEnum.QKDAPPSTATUS_ZOMBIE
-
+    OUT_OF_TIME = QKDAppStatusEnum.QKDAPPSTATUS_OUT_OF_TIME
+    ZOMBIE = QKDAppStatusEnum.QKDAPPSTATUS_ZOMBIE
 
+# Function to map between gRPC and ORM enums.
 grpc_to_enum__qkd_app_status = functools.partial(
-    grpc_to_enum, QKDAppStatusEnum, ORM_QKDAppStatusEnum)
+    grpc_to_enum, QKDAppStatusEnum, ORM_QKDAppStatusEnum
+)
diff --git a/src/qkd_app/service/database/models/enums/QKDAppTypes.py b/src/qkd_app/service/database/models/enums/QKDAppTypes.py
index f50b8982d80c0af97c2cbd96d336f450afc50f9b..fdd318cec49b7b7ec8e1a0347c8b0f8c1aeb3a5c 100644
--- a/src/qkd_app/service/database/models/enums/QKDAppTypes.py
+++ b/src/qkd_app/service/database/models/enums/QKDAppTypes.py
@@ -12,14 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import enum, functools
+import enum
+import functools
 from common.proto.qkd_app_pb2 import QKDAppTypesEnum
 from ._GrpcToEnum import grpc_to_enum
 
+# Enum mapping for ORM-based app types.
 class ORM_QKDAppTypesEnum(enum.Enum):
     INTERNAL = QKDAppTypesEnum.QKDAPPTYPES_INTERNAL
-    CLIENT   = QKDAppTypesEnum.QKDAPPTYPES_CLIENT
-
+    CLIENT = QKDAppTypesEnum.QKDAPPTYPES_CLIENT
 
+# Function to map between gRPC and ORM enums.
 grpc_to_enum__qkd_app_types = functools.partial(
-    grpc_to_enum, QKDAppTypesEnum, ORM_QKDAppTypesEnum)
+    grpc_to_enum, QKDAppTypesEnum, ORM_QKDAppTypesEnum
+)
diff --git a/src/qkd_app/service/database/uuids/QKDApp.py b/src/qkd_app/service/database/uuids/QKDApp.py
index 175f1d5f3cf4ceda12a022b4afadb376e11ae5a5..d451dcd768ae42140e60a148708f2704a8a96c82 100644
--- a/src/qkd_app/service/database/uuids/QKDApp.py
+++ b/src/qkd_app/service/database/uuids/QKDApp.py
@@ -16,15 +16,22 @@ from common.proto.qkd_app_pb2 import AppId
 from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
 from ._Builder import get_uuid_from_string, get_uuid_random
 
-def app_get_uuid(
-    app_id : AppId, allow_random : bool = False
-) -> str:
+def app_get_uuid(app_id: AppId, allow_random: bool = False) -> str:
+    """
+    Retrieves or generates the UUID for an app.
+    
+    :param app_id: AppId object that contains the app UUID
+    :param allow_random: If True, generates a random UUID if app_uuid is not set
+    :return: App UUID as a string
+    """
     app_uuid = app_id.app_uuid.uuid
 
-    if len(app_uuid) > 0:
+    if app_uuid:
         return get_uuid_from_string(app_uuid)
-    if allow_random: return get_uuid_random()
+    
+    if allow_random:
+        return get_uuid_random()
 
     raise InvalidArgumentsException([
         ('app_id.app_uuid.uuid', app_uuid),
-    ], extra_details=['At least one is required to produce a App UUID'])
+    ], extra_details=['At least one UUID is required to identify the app.'])
\ No newline at end of file
diff --git a/src/qkd_app/service/database/uuids/_Builder.py b/src/qkd_app/service/database/uuids/_Builder.py
index 39c98de69d577ce2722693e57c4ee678124f9e30..c5996b0f9d2cced27fd05e3966c3a60fa9bae24d 100644
--- a/src/qkd_app/service/database/uuids/_Builder.py
+++ b/src/qkd_app/service/database/uuids/_Builder.py
@@ -15,30 +15,37 @@
 from typing import Optional, Union
 from uuid import UUID, uuid4, uuid5
 
-# Generate a UUIDv5-like from the SHA-1 of "TFS" and no namespace to be used as the NAMESPACE for all
-# the context UUIDs generated. For efficiency purposes, the UUID is hardcoded; however, it is produced
-# using the following code:
-#    from hashlib import sha1
-#    from uuid import UUID
-#    hash = sha1(bytes('TFS', 'utf-8')).digest()
-#    NAMESPACE_TFS = UUID(bytes=hash[:16], version=5)
+# Hardcoded namespace for generating UUIDs.
 NAMESPACE_TFS = UUID('200e3a1f-2223-534f-a100-758e29c37f40')
 
-def get_uuid_from_string(str_uuid_or_name : Union[str, UUID], prefix_for_name : Optional[str] = None) -> str:
-    # if UUID given, assume it is already a valid UUID
-    if isinstance(str_uuid_or_name, UUID): return str_uuid_or_name
+def get_uuid_from_string(str_uuid_or_name: Union[str, UUID], prefix_for_name: Optional[str] = None) -> str:
+    """
+    Convert a string or UUID object into a UUID string. If input is a name, generate a UUID using the TFS namespace.
+    
+    :param str_uuid_or_name: Input string or UUID to be converted into UUID format
+    :param prefix_for_name: Optional prefix to add before the name when generating a name-based UUID
+    :return: A valid UUID string
+    :raises ValueError: If the input is invalid and cannot be converted to a UUID
+    """
+    if isinstance(str_uuid_or_name, UUID):
+        return str(str_uuid_or_name)  # Ensure returning a string representation
+
     if not isinstance(str_uuid_or_name, str):
-        MSG = 'Parameter({:s}) cannot be used to produce a UUID'
-        raise Exception(MSG.format(str(repr(str_uuid_or_name))))
+        raise ValueError(f"Invalid parameter ({repr(str_uuid_or_name)}). Expected a string or UUID to produce a valid UUID.")
+
     try:
-        # try to parse as UUID
+        # Try to interpret the input as a UUID
         return str(UUID(str_uuid_or_name))
-    except: # pylint: disable=bare-except
-        # produce a UUID within TFS namespace from parameter
-        if prefix_for_name is not None:
-            str_uuid_or_name = '{:s}/{:s}'.format(prefix_for_name, str_uuid_or_name)
+    except ValueError:
+        # If the input isn't a valid UUID, generate one using the name-based approach
+        if prefix_for_name:
+            str_uuid_or_name = f"{prefix_for_name}/{str_uuid_or_name}"
         return str(uuid5(NAMESPACE_TFS, str_uuid_or_name))
 
 def get_uuid_random() -> str:
-    # Generate random UUID. No need to use namespace since "namespace + random = random".
+    """
+    Generate and return a new random UUID as a string.
+    
+    :return: A randomly generated UUID string
+    """
     return str(uuid4())
diff --git a/src/qkd_app/service/rest_server/RestServer.py b/src/qkd_app/service/rest_server/RestServer.py
index e21531c5bcf0e1cf15a8f08952d6325a8349f398..b2b830a8171b666eaafa695a1877868ce562b672 100644
--- a/src/qkd_app/service/rest_server/RestServer.py
+++ b/src/qkd_app/service/rest_server/RestServer.py
@@ -17,7 +17,27 @@ from common.Settings import get_service_baseurl_http, get_service_port_http
 from common.tools.service.GenericRestServer import GenericRestServer
 
 class RestServer(GenericRestServer):
+    """
+    REST server class for handling HTTP requests related to QKD applications.
+    
+    Productivity Improvements:
+    - Simplifies initialization by automatically fetching the required service details (base URL and port).
+    - Extends a generic REST server class, ensuring reusability and consistency across services.
+    """
     def __init__(self, cls_name: str = __name__) -> None:
+        """
+        Initialize the REST server with service-specific configuration.
+        
+        - Retrieves the port and base URL dynamically based on the QKD_APP service.
+        - Inherits from `GenericRestServer` for common REST functionality, reducing code duplication.
+        
+        Arguments:
+        cls_name (str): Name of the current class (default: `__name__`).
+        """
+        # Dynamically fetch the HTTP bind port and base URL for the QKD_APP service
         bind_port = get_service_port_http(ServiceNameEnum.QKD_APP)
         base_url = get_service_baseurl_http(ServiceNameEnum.QKD_APP)
+        
+        # Pass these values to the parent `GenericRestServer` class for initialization
         super().__init__(bind_port, base_url, cls_name=cls_name)
+
diff --git a/src/qkd_app/service/rest_server/qkd_app/Resources.py b/src/qkd_app/service/rest_server/qkd_app/Resources.py
index 6ba79d3940da91dfebc1a1c666893548caccbe6c..d14fe9575b21319a0fa597a7746510a11e102903 100644
--- a/src/qkd_app/service/rest_server/qkd_app/Resources.py
+++ b/src/qkd_app/service/rest_server/qkd_app/Resources.py
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import uuid, json
+import uuid
+import json
 from flask import request
 from flask_restful import Resource
 from common.proto.context_pb2 import Empty
@@ -21,7 +22,6 @@ from common.Constants import DEFAULT_CONTEXT_NAME
 from context.client.ContextClient import ContextClient
 from qkd_app.client.QKDAppClient import QKDAppClient
 
-
 class _Resource(Resource):
     def __init__(self) -> None:
         super().__init__()
@@ -32,18 +32,56 @@ class Index(_Resource):
     def get(self):
         return {'hello': 'world'}
 
+class ListDevices(_Resource):
+    def get(self):
+        """
+        List devices and associate the apps with them.
+        """
+        devices = self.context_client.ListDevices(Empty()).devices
+        for device in devices:
+            # Fetch apps associated with this device
+            device.apps = self.get_apps_for_device(device.device_id.device_uuid.uuid)
+        return {'devices': [self.format_device(device) for device in devices]}
+
+    def get_apps_for_device(self, device_uuid):
+        """
+        Fetch the apps associated with a given device UUID.
+        """
+        try:
+            # Call the AppService to get the list of apps
+            apps_list = self.qkd_app_client.ListApps(Empty())
+            
+            # Filter apps for this specific device
+            device_apps = []
+            for app in apps_list.apps:
+                if app.local_device_id.device_uuid.uuid == device_uuid or \
+                   app.remote_device_id.device_uuid.uuid == device_uuid:
+                    device_apps.append(app)
+            return device_apps
+        
+        except Exception as e:
+            print(f"Error fetching apps for device {device_uuid}: {e}")
+            return []
+
+    def format_device(self, device):
+        """
+        Formats a device object to include the associated apps in the response.
+        """
+        return {
+            'device_uuid': device.device_id.device_uuid.uuid,
+            'name': device.name,
+            'type': device.device_type,
+            'status': device.device_operational_status,
+            'apps': [{'app_id': app.app_id.app_uuid.uuid, 'app_status': app.app_status, 'app_type': app.app_type} for app in device.apps]
+        }
+
 class CreateQKDApp(_Resource):
-    # Optare: Post request for the QKD Node to call the TeraflowSDN. Example of requests below
     def post(self):
         app = request.get_json()['app']
-
-        devices = self.context_client.ListDevices(Empty())
-        devices = devices.devices
-
+        devices = self.context_client.ListDevices(Empty()).devices
         local_device = None
 
-
-        # This for-loop won't be necessary if we can garantee Device ID is the same as QKDN Id
+        # This for-loop won't be necessary if Device ID is guaranteed to be the same as QKDN Id
         for device in devices:
             for config_rule in device.device_config.config_rules:
                 if config_rule.custom.resource_key == '__node__':
@@ -53,15 +91,6 @@ class CreateQKDApp(_Resource):
                         local_device = device
                     break
 
-        # Optare: Todo:  Verify that a service is present for this app
-        '''
-        requests.post('http://10.211.36.220/app/create_qkd_app', json={'app': {'server_app_id':'1', 'client_app_id':[], 'app_status':'ON', 'local_qkdn_id':'00000001-0000-0000-0000-000000000000', 'backing_qkdl_id':['00000003-0002-0000-0000-000000000000']}})
-
-
-        requests.post('http://10.211.36.220/app/create_qkd_app', json={'app': {'server_app_id':'1', 'client_app_id':[], 'app_status':'ON', 'local_qkdn_id':'00000003-0000-0000-0000-000000000000', 'backing_qkdl_id':['00000003-0002-0000-0000-000000000000']}})
-        '''
-        
-
         if local_device is None:
             return {"status": "fail"}
 
@@ -76,11 +105,7 @@ class CreateQKDApp(_Resource):
             'remote_device_id': {'device_uuid': {'uuid': ''}},
         }
 
-
-        # Optare: This will call our internal RegisterApp which supports the creation of both internal and external app.
-        # Optare the verification for knowing if two parties are requesting the same app is done inside RegisterApp's function
         self.qkd_app_client.RegisterApp(App(**external_app_src_dst))
 
-        # Optare: Todo: Communicate by SBI with both Nodes of the new App
-
         return {"status": "success"}
+
diff --git a/src/service/service/service_handlers/qkd/qkd_service_handler.py b/src/service/service/service_handlers/qkd/qkd_service_handler.py
index 0977388005ef72fe036de93de2dc73438f0c6163..2bfbcb59dd045d10b9267dc15119e1d17e1929d4 100644
--- a/src/service/service/service_handlers/qkd/qkd_service_handler.py
+++ b/src/service/service/service_handlers/qkd/qkd_service_handler.py
@@ -17,7 +17,8 @@ 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.qkd_app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum
+from common.proto.qkd_app_pb2 import App, AppId, QKDAppStatusEnum, QKDAppTypesEnum
+from common.proto.context_pb2 import ContextId, Uuid
 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
@@ -41,6 +42,7 @@ class QKDServiceHandler(_ServiceHandler):
         self.__service = service
         self.__task_executor = task_executor
         self.__settings_handler = SettingsHandler(service.service_config, **settings)
+        self.qkd_app_client = task_executor._qkd_app_client  # Initialize qkd_app_client
 
 
     # Optare: This function is where the service is created
@@ -88,14 +90,9 @@ class QKDServiceHandler(_ServiceHandler):
                 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
 
@@ -271,7 +268,7 @@ class QKDServiceHandler(_ServiceHandler):
                     'remote_device_id': dst_device.device_id,
                 }
 
-                self.__task_executor.register_app(App(**internal_app_src_dst))
+                self.__task_executor.register_qkd_app(App(**internal_app_src_dst))
                 
 
                 # Register App
@@ -286,7 +283,7 @@ class QKDServiceHandler(_ServiceHandler):
                     'remote_device_id': src_device.device_id,
                 }
 
-                self.__task_executor.register_app(App(**internal_app_dst_src))
+                self.__task_executor.register_qkd_app(App(**internal_app_dst_src))
 
             results.append(True)
         except Exception as e: # pylint: disable=broad-except
@@ -297,31 +294,127 @@ class QKDServiceHandler(_ServiceHandler):
 
     # Optare: This will be to delete a service
     def DeleteEndpoint(
-        self, endpoints : List[Tuple[str, str, Optional[str]]],
-        connection_uuid : Optional[str] = None
+        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()
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0:
+            return []
+
+        LOGGER.info(f'Deleting Endpoints: {endpoints}')
+        LOGGER.info(f'Connection UUID: {connection_uuid}')
+
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        context_uuid = self.__service.service_id.context_id.context_uuid.uuid
+        LOGGER.info(f'Service UUID: {service_uuid}, Context UUID: {context_uuid}')
+
+        results = []
+        apps = list()  # Initialize apps as an empty list, in case fetching fails
+        try:
+            # Initialize device lists and QKDN IDs
+            devices = []
+            qkdn_ids = []
+            interfaces = []
+            links = []
+
+            # Populate devices and QKDN ids from endpoints
+            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)))
+                LOGGER.info(f'Device: {device}, Endpoint Left: {endpoint_left_uuid}, Endpoint Right: {endpoint_right_uuid}')
+
+                devices.append(device)
+                interfaces.append([0, 0])
+                links.append([])
+
+                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']
+                            if endpoint_str == endpoint_left_uuid:
+                                interfaces[idx][0] = value['qkdi_id']
+                            elif endpoint_str == endpoint_right_uuid:
+                                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(f'Interfaces: {interfaces}, Links: {links}, QKDN IDs: {qkdn_ids}')
+
+            # Fetch the related apps for the service using the same pattern as in routes.py
+            try:
+                context_id = ContextId(context_uuid=Uuid(uuid=context_uuid))
+                apps_response = self.__task_executor._qkd_app_client.ListApps(context_id)
+                apps = apps_response.apps  # Assign the apps to the list, if successful
+                LOGGER.info(f"Apps retrieved: {apps}")
+            except grpc.RpcError as e:
+                LOGGER.error(f"gRPC error while fetching apps: {e.details()}")
+                if e.code() != grpc.StatusCode.NOT_FOUND: 
+                    raise
+                apps = list()  # If an error occurs, ensure `apps` is still an empty list
+
+            # Filter related internal apps
+            related_apps = [
+                app for app in apps
+                if app.server_app_id == service_uuid and app.app_type == QKDAppTypesEnum.QKDAPPTYPES_INTERNAL
+            ]
+
+            # Log each app's details
+            for app in related_apps:
+                LOGGER.info(f"App ID: {app.app_id.app_uuid.uuid}, Status: {app.app_status}")
+
+            # Update each app status to DISCONNECTED before deletion
+            for app in related_apps:
+                self.__task_executor.update_qkd_app_status(app, QKDAppStatusEnum.QKDAPPSTATUS_DISCONNECTED)
+
+            results.append(True)
+
+        except Exception as e:  # pylint: disable=broad-except
+            LOGGER.error(f"Failed to delete QKD service: {str(e)}")
+            results.append(e)
+
+        return results
+
+    def fetch_related_internal_apps(self, context_uuid: str, service_uuid: str) -> List[App]:
+        try:
+            context_id = ContextId(context_uuid=Uuid(uuid=context_uuid))
+            apps_response = self.qkd_app_client.ListApps(context_id)
+            
+            # Log the apps retrieved to ensure they exist and have a status
+            LOGGER.info(f"Apps retrieved: {apps_response.apps}")
+            
+            internal_apps = [
+                app for app in apps_response.apps
+                if app.app_type == QKDAppTypesEnum.QKDAPPTYPES_INTERNAL 
+                and app.server_app_id == service_uuid
+                and app.app_status == QKDAppStatusEnum.ACTIVE  # Ensure you are checking status
+            ]
+            
+            LOGGER.info(f"Filtered internal apps: {internal_apps}")
+            return internal_apps
+
+        except Exception as e:
+            LOGGER.error(f"Error fetching related internal apps: {e}")
+            return []
 
     # 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]]:
+    def SetConstraint(self, constraints: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         """ Create/Update service constraints.
             Parameters:
                 constraints: List[Tuple[str, Any]]
@@ -335,10 +428,30 @@ class QKDServiceHandler(_ServiceHandler):
                     returned; otherwise, the Exception that is raised during
                     the processing must be returned.
         """
-        raise NotImplementedError()
+        results = []
+        try:
+            for constraint_type, constraint_value in constraints:
+                LOGGER.info(f"Setting constraint: {constraint_type} with value: {constraint_value}")
+
+                # Assuming you store constraints as part of service config rules
+                constraint_key = f"/constraints/{constraint_type}"
+                json_config_rule = json_config_rule_set(constraint_key, constraint_value)
 
-    def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \
-            -> List[Union[bool, Exception]]:
+                # Apply the configuration rule to the service
+                self.__service.service_config.config_rules.append(ConfigRule(**json_config_rule))
+
+            # Reconfigure the service with new constraints
+            self.__task_executor.configure_service(self.__service)
+
+            results.append(True)
+
+        except Exception as e:
+            LOGGER.error(f"Failed to set constraints: {str(e)}")
+            results.append(e)
+
+        return results
+
+    def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
         """ Delete service constraints.
             Parameters:
                 constraints: List[Tuple[str, Any]]
@@ -354,16 +467,37 @@ class QKDServiceHandler(_ServiceHandler):
                     be returned; otherwise, the Exception that is raised during
                     the processing must be returned.
         """
-        raise NotImplementedError()
+        results = []
+        try:
+            for constraint_type, _ in constraints:
+                LOGGER.info(f"Deleting constraint: {constraint_type}")
+
+                # Remove the constraint from the service config rules
+                constraint_key = f"/constraints/{constraint_type}"
+                json_config_rule = json_config_rule_delete(constraint_key)
+
+                for rule in self.__service.service_config.config_rules:
+                    if rule.custom.resource_key == constraint_key:
+                        self.__service.service_config.config_rules.remove(rule)
+
+            # Reconfigure the service after removing constraints
+            self.__task_executor.configure_service(self.__service)
+
+            results.append(True)
+
+        except Exception as e:
+            LOGGER.error(f"Failed to delete constraints: {str(e)}")
+            results.append(e)
 
-    def SetConfig(self, resources: List[Tuple[str, Any]]) \
-            -> List[Union[bool, Exception]]:
+        return results
+
+    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.
+                    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.
@@ -372,10 +506,28 @@ class QKDServiceHandler(_ServiceHandler):
                     returned; otherwise, the Exception that is raised during
                     the processing must be returned.
         """
-        raise NotImplementedError()
+        results = []
+        try:
+            for resource_key, resource_value in resources:
+                LOGGER.info(f"Setting config: {resource_key} with value: {resource_value}")
+
+                json_config_rule = json_config_rule_set(resource_key, resource_value)
+
+                # Apply the configuration rule to the service
+                self.__service.service_config.config_rules.append(ConfigRule(**json_config_rule))
+
+            # Reconfigure the service with new configurations
+            self.__task_executor.configure_service(self.__service)
+
+            results.append(True)
+
+        except Exception as e:
+            LOGGER.error(f"Failed to set config: {str(e)}")
+            results.append(e)
+
+        return results
 
-    def DeleteConfig(self, resources: List[Tuple[str, Any]]) \
-            -> List[Union[bool, Exception]]:
+    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]]
@@ -391,4 +543,25 @@ class QKDServiceHandler(_ServiceHandler):
                     be returned; otherwise, the Exception that is raised during
                     the processing must be returned.
         """
-        raise NotImplementedError()
+        results = []
+        try:
+            for resource_key, _ in resources:
+                LOGGER.info(f"Deleting config: {resource_key}")
+
+                json_config_rule = json_config_rule_delete(resource_key)
+
+                # Remove the matching configuration rule
+                for rule in self.__service.service_config.config_rules:
+                    if rule.custom.resource_key == resource_key:
+                        self.__service.service_config.config_rules.remove(rule)
+
+            # Reconfigure the service after deleting configurations
+            self.__task_executor.configure_service(self.__service)
+
+            results.append(True)
+
+        except Exception as e:
+            LOGGER.error(f"Failed to delete config: {str(e)}")
+            results.append(e)
+
+        return results
diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py
index cb27993702963b4aac88ec04eca2a1c796d0c364..67f6a516c9bc3031b5cdd2aed80cd6fdd7e1c9c2 100644
--- a/src/service/service/task_scheduler/TaskExecutor.py
+++ b/src/service/service/task_scheduler/TaskExecutor.py
@@ -16,11 +16,14 @@ import json, logging
 from enum import Enum
 from typing import TYPE_CHECKING, Any, Dict, Optional, Union
 from common.method_wrappers.ServiceExceptions import NotFoundException
+from typing import List
+from common.proto.qkd_app_pb2 import QKDAppStatusEnum
 from common.proto.context_pb2 import (
     Connection, ConnectionId, Device, DeviceDriverEnum, DeviceId, Service, ServiceId,
     OpticalConfig, OpticalConfigId
 )
-from common.proto.qkd_app_pb2 import App
+from common.proto.qkd_app_pb2 import App, AppId
+from common.proto.context_pb2 import ContextId
 from common.tools.context_queries.Connection import get_connection_by_id
 from common.tools.context_queries.Device import get_device
 from common.tools.context_queries.Service import get_service_by_id
@@ -33,7 +36,7 @@ from service.service.service_handler_api.Exceptions import (
     UnsatisfiedFilterException, UnsupportedFilterFieldException, UnsupportedFilterFieldValueException
 )
 from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory, get_service_handler_class
-from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key, get_app_key
+from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key, get_qkd_app_key
 
 if TYPE_CHECKING:
     from service.service.service_handler_api._ServiceHandler import _ServiceHandler
@@ -229,8 +232,56 @@ class TaskExecutor:
 
     # ----- QkdApp-related methods -------------------------------------------------------------------------------------
 
-    def register_app(self, app: App) -> None:
-        app_key = get_app_key(app.app_id)
+    def register_qkd_app(self, app: App) -> None:
+        """
+        Registers a QKD App and stores it in the cache.
+        """
+        qkd_app_key = get_qkd_app_key(app.app_id)
         self._qkd_app_client.RegisterApp(app)
-        LOGGER.info("reg registered")
-        self._store_grpc_object(CacheableObjectType.QKD_APP, app_key, app)
+        LOGGER.info("QKD app registered with key: %s", qkd_app_key)
+        self._store_grpc_object(CacheableObjectType.QKD_APP, qkd_app_key, app)
+
+    def update_qkd_app_status(self, app: App, new_status: QKDAppStatusEnum) -> None:
+        """
+        Updates the status of a QKD app and persists it to the database.
+        """
+        try:
+            app.app_status = new_status
+            LOGGER.info(f"Attempting to update app {app.app_id.app_uuid.uuid} to status {new_status}")
+            self._qkd_app_client.UpdateApp(app)
+            LOGGER.info(f"Successfully updated app {app.app_id.app_uuid.uuid} to status {new_status}")
+        except Exception as e:
+            LOGGER.error(f"Failed to update QKD app {app.app_id.app_uuid.uuid}: {str(e)}")
+            raise e
+
+    def list_qkd_apps(self, context_id: ContextId) -> List[App]:
+        """
+        Retrieves a list of QKD apps from the QKD App service.
+        """
+        try:
+            apps_response = self._qkd_app_client.ListApps(context_id)
+            LOGGER.info(f"ListApps retrieved: {len(apps_response.apps)} apps with status")
+            
+            # Ensure that the status is logged and used
+            for app in apps_response.apps:
+                LOGGER.info(f"App ID: {app.app_id.app_uuid.uuid}, Status: {app.app_status}")
+            
+            return apps_response.apps
+        except Exception as e:
+            LOGGER.error(f"Failed to list QKD apps: {str(e)}")
+            return []
+
+    def delete_qkd_app(self, app_id: AppId) -> None:
+        """
+        Deletes a QKD App by its AppId and removes it from the cache.
+        """
+        qkd_app_key = get_qkd_app_key(app_id)
+        try:
+            LOGGER.info(f"Attempting to delete QKD app with AppId: {app_id}")
+            self._qkd_app_client.DeleteApp(app_id)
+            LOGGER.info(f"QKD app deleted with key: {qkd_app_key}")
+            self._delete_grpc_object(CacheableObjectType.QKD_APP, qkd_app_key)
+        except Exception as e:
+            LOGGER.error(f"Failed to delete QKD app with AppId {app_id}: {str(e)}")
+            raise e
+
diff --git a/src/service/service/tools/ObjectKeys.py b/src/service/service/tools/ObjectKeys.py
index cfc719bba736a4ea0789b028a97ca267b2d04089..f67cb02e143b78127484d6644a7fdd8c9c71e29c 100644
--- a/src/service/service/tools/ObjectKeys.py
+++ b/src/service/service/tools/ObjectKeys.py
@@ -26,6 +26,6 @@ def get_service_key(service_id : ServiceId) -> str:
     service_uuid = service_id.service_uuid.uuid
     return '{:s}/{:s}'.format(context_uuid, service_uuid)
 
-def get_app_key(app_id : AppId) -> str:
+def get_qkd_app_key(app_id: AppId) -> str:
     return app_id.app_uuid.uuid
 
diff --git a/src/tests/tools/mock_qkd_nodes/start.sh b/src/tests/tools/mock_qkd_nodes/start.sh
index faf2f84baf61f16565b497b53bf5f41f45007c00..89797b9c9496cdf58061c406ad2886be0d9c47f6 100755
--- a/src/tests/tools/mock_qkd_nodes/start.sh
+++ b/src/tests/tools/mock_qkd_nodes/start.sh
@@ -13,18 +13,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+#!/bin/bash
 cd "$(dirname "$0")"
 
+# Function to kill all background processes
 killbg() {
-        for p in "${pids[@]}" ; do
-                kill "$p";
-        done
+    for p in "${pids[@]}" ; do
+        kill "$p";
+    done
 }
 
 trap killbg EXIT
 pids=()
+
+# Set FLASK_APP and run the Flask instances on different ports
+export FLASK_APP=wsgi
 flask run --host 0.0.0.0 --port 11111 & 
 pids+=($!)
+
 flask run --host 0.0.0.0 --port 22222 & 
 pids+=($!)
-flask run --host 0.0.0.0 --port 33333
+
+flask run --host 0.0.0.0 --port 33333 & 
+pids+=($!)
+
+# Wait for all background processes to finish
+wait
+
diff --git a/src/tests/tools/mock_qkd_nodes/wsgi.py b/src/tests/tools/mock_qkd_nodes/wsgi.py
index 3f8847849337fbfb1a9f84c783786218db4fb04d..ea25b43010bc3915df88044ad2c494d70d5419ed 100644
--- a/src/tests/tools/mock_qkd_nodes/wsgi.py
+++ b/src/tests/tools/mock_qkd_nodes/wsgi.py
@@ -23,7 +23,7 @@ yang_validator = YangValidator('etsi-qkd-sdn-node', ['etsi-qkd-node-types'])
 
 
 nodes = {
-    '10.0.2.10:11111': {'node': {
+    '10.211.36.220:11111': {'node': {
             'qkdn_id': '00000001-0000-0000-0000-000000000000',
         },
         'qkdn_capabilities': {
@@ -54,7 +54,7 @@ nodes = {
                 {
                     'qkdi_id': '101',
                     'qkdi_att_point': {
-                        'device':'10.0.2.10',
+                        'device':'10.211.36.220',
                         'port':'1001'
                     },
                     'qkdi_capabilities': {
@@ -69,7 +69,7 @@ nodes = {
         }
     },
 
-    '10.0.2.10:22222': {'node': {
+    '10.211.36.220:22222': {'node': {
             'qkdn_id': '00000002-0000-0000-0000-000000000000',
         },
         'qkdn_capabilities': {
@@ -100,7 +100,7 @@ nodes = {
                 {
                     'qkdi_id': '201',
                     'qkdi_att_point': {
-                        'device':'10.0.2.10',
+                        'device':'10.211.36.220',
                         'port':'2001'
                     },
                     'qkdi_capabilities': {
@@ -109,7 +109,7 @@ nodes = {
                 {
                     'qkdi_id': '202',
                     'qkdi_att_point': {
-                        'device':'10.0.2.10',
+                        'device':'10.211.36.220',
                         'port':'2002'
                     },
                     'qkdi_capabilities': {
@@ -124,7 +124,7 @@ nodes = {
         }
     },
 
-    '10.0.2.10:33333': {'node': {
+    '10.211.36.220:33333': {'node': {
             'qkdn_id': '00000003-0000-0000-0000-000000000000',
         },
         'qkdn_capabilities': {
@@ -155,7 +155,7 @@ nodes = {
                 {
                     'qkdi_id': '301',
                     'qkdi_att_point': {
-                        'device':'10.0.2.10',
+                        'device':'10.211.36.220',
                         'port':'3001'
                     },
                     'qkdi_capabilities': {
diff --git a/src/webui/service/qkd_app/routes.py b/src/webui/service/qkd_app/routes.py
index 71243fb75e552ec5568eedacdcadabbc39516b4e..2037c288d36cde6ced38d0c5416041aaad17cdf1 100644
--- a/src/webui/service/qkd_app/routes.py
+++ b/src/webui/service/qkd_app/routes.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS)
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,21 +12,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import grpc, json, logging
-
+import grpc
+import 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.proto.qkd_app_pb2 import App, AppId, QKDAppStatusEnum, QKDAppTypesEnum
+from common.proto.context_pb2 import Uuid, ContextId
 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
 
-
+# Set up logging
 LOGGER = logging.getLogger(__name__)
+
+# Blueprint for QKDApp routes
 qkd_app = Blueprint('qkd_app', __name__, url_prefix='/qkd_app')
 
+# Initialize clients
 qkd_app_client = QKDAppClient()
 context_client = ContextClient()
 
@@ -35,79 +37,131 @@ 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']
 
+    # Connect to context client
     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
+    try:
+
+        # Fetch context object
+        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:
-            # 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(
-        'qkd_app/home.html', apps=apps, device_names=device_names, ate=QKDAppTypesEnum, ase=QKDAppStatusEnum)
+            try:
+                # Call ListApps using the context_id
+                apps_response = qkd_app_client.ListApps(context_obj.context_id)
+                apps = apps_response.apps
+            except grpc.RpcError as e:
+                LOGGER.error(f"gRPC error while fetching apps: {e.details()}")
+                if e.code() != grpc.StatusCode.NOT_FOUND: raise
+                if e.details() != 'Context({:s}) not found'.format(context_uuid): raise
+                apps = list()
+            else:
+                # Map local and remote device names
+                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:
+                            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:
+                            device_names[app.remote_device_id.device_uuid.uuid] = device.name
+    finally:
+        context_client.close()
+
+    # Render the template with app list and device names
+    return render_template(
+        'qkd_app/home.html', 
+        apps=apps, 
+        device_names=device_names, 
+        ate=QKDAppTypesEnum, 
+        ase=QKDAppStatusEnum
+    )
 
-@qkd_app.route('detail/<path:app_uuid>', methods=('GET', 'POST'))
+@qkd_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
+    """
+    Displays details for a specific QKD app identified by its UUID.
+    """
+    try:
+        qkd_app_client.connect()
+
+        # Wrap the app_uuid in a Uuid object and fetch details
+        uuid_message = Uuid(uuid=app_uuid)
+        app_id = AppId(app_uuid=uuid_message)
+        app_detail = qkd_app_client.GetApp(app_id)
+
+        if not app_detail:
+            flash(f"App with UUID {app_uuid} not found", "danger")
+            return redirect(url_for("qkd_app.home"))
+
+        # Fetch device details
+        context_client.connect()
+        device_names = {}
+
+        try:
+            if app_detail.local_device_id.device_uuid.uuid:
+                local_device = get_device(context_client, app_detail.local_device_id.device_uuid.uuid)
+                if local_device:
+                    device_names[app_detail.local_device_id.device_uuid.uuid] = local_device.name
+
+            if app_detail.remote_device_id.device_uuid.uuid:
+                remote_device = get_device(context_client, app_detail.remote_device_id.device_uuid.uuid)
+                if remote_device:
+                    device_names[app_detail.remote_device_id.device_uuid.uuid] = remote_device.name
+
+        except grpc.RpcError as e:
+            LOGGER.error(f"Failed to retrieve device details for app {app_uuid}: {e}")
+            flash(f"Error retrieving device details: {e.details()}", "danger")
+            return redirect(url_for("qkd_app.home"))
+
+        finally:
+            context_client.close()
+
+        return render_template(
+            'qkd_app/detail.html', 
+            app=app_detail, 
+            ase=QKDAppStatusEnum, 
+            ate=QKDAppTypesEnum, 
+            device_names=device_names
+        )
+
+    except grpc.RpcError as e:
+        LOGGER.error(f"Failed to retrieve app details for {app_uuid}: {e}")
+        flash(f"Error retrieving app details: {e.details()}", "danger")
+        return redirect(url_for("qkd_app.home"))
+
+    finally:
+        qkd_app_client.close()
 
 @qkd_app.get('<path:app_uuid>/delete')
-def delete(app_uuid):
-    '''
+def delete(app_uuid: str):
+    """
+    Deletes a specific QKD app identified by its UUID.
+    """
     try:
+        request = AppId(app_uuid=Uuid(uuid=app_uuid))
 
-        # first, check if link exists!
-        # request: LinkId = LinkId()
-        # request.link_uuid.uuid = link_uuid
-        # response: Link = client.GetLink(request)
-        # TODO: finalize implementation
+        qkd_app_client.connect()
+        qkd_app_client.DeleteApp(request)  # Call the DeleteApp method
+        qkd_app_client.close()
 
-        request = LinkId()
-        request.link_uuid.uuid = link_uuid # pylint: disable=no-member
-        context_client.connect()
-        context_client.RemoveLink(request)
-        context_client.close()
+        flash(f'App "{app_uuid}" deleted successfully!', 'success')
+
+    except grpc.RpcError as e:
+        LOGGER.error(f"Problem deleting app {app_uuid}: {e}")
+        flash(f"Problem deleting app {app_uuid}: {e.details()}", 'danger')
+
+    except Exception as e:
+        LOGGER.exception(f"Unexpected error while deleting app {app_uuid}: {e}")
+        flash(f"Unexpected error: {str(e)}", 'danger')
 
-        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
+    return redirect(url_for('qkd_app.home'))
diff --git a/src/webui/service/templates/qkd_app/detail.html b/src/webui/service/templates/qkd_app/detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..c483af70968e2dca32d01bddac2d3c12230ae45f
--- /dev/null
+++ b/src/webui/service/templates/qkd_app/detail.html
@@ -0,0 +1,126 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<h1>App {{ app.app_id.app_uuid.uuid }}</h1>
+
+<!-- Action Buttons -->
+<div class="row mb-3">
+    <div class="col-sm-3">
+        <button type="button" class="btn btn-success" onclick="window.location.href='{{ url_for('qkd_app.home') }}';">
+            <i class="bi bi-box-arrow-in-left"></i> Back to app list
+        </button>
+    </div>
+    <div class="col-sm-3">
+        <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
+            <i class="bi bi-x-square"></i> Delete app
+        </button>
+    </div>
+</div>
+
+<!-- App Basic Info -->
+<div class="row mb-3">
+    <div class="col-sm-4">
+        <b>UUID: </b>{{ app.app_id.app_uuid.uuid }}<br>
+        <b>Status: </b>{{ ase.Name(app.app_status).replace('QKDAPPSTATUS_', '') }}<br>
+        <b>Type: </b>{{ ate.Name(app.app_type).replace('QKDAPPTYPES_', '').replace('CLIENT', 'EXTERNAL') }}<br>
+    </div>
+
+    <!-- Associated Devices -->
+    <div class="col-sm-8">
+        <h5>Associated Devices</h5>
+        <table class="table table-striped table-hover">
+            <thead>
+                <tr>
+                    <th scope="col">Device</th>
+                    <th scope="col">Endpoint Type</th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr>
+                    <td>
+                        <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 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/>
+                                <path d="M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                    <td>Local Device</td>
+                </tr>
+                {% if app.remote_device_id.device_uuid.uuid %}
+                <tr>
+                    <td>
+                        <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 8z"/>
+                                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z"/>
+                                <path d="M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
+                            </svg>
+                        </a>
+                    </td>
+                    <td>Remote Device</td>
+                </tr>
+                {% endif %}
+            </tbody>
+        </table>
+    </div>
+</div>
+
+<!-- QoS -->
+<div class="row mb-3">
+    <div class="col-sm-12">
+        <h5>App QoS</h5>
+        <table class="table table-striped">
+            <thead>
+                <tr>
+                    <th scope="col">QoS Parameter</th>
+                    <th scope="col">Value</th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr>
+                    <td>Max Bandwidth</td>
+                    <td>{{ app.qos.max_bandwidth }} bps</td>
+                </tr>
+                <tr>
+                    <td>Min Bandwidth</td>
+                    <td>{{ app.qos.min_bandwidth or 'N/A' }} bps</td>
+                </tr>
+                <tr>
+                    <td>Jitter</td>
+                    <td>{{ app.qos.jitter or 'N/A' }} ms</td>
+                </tr>
+                <tr>
+                    <td>TTL</td>
+                    <td>{{ app.qos.ttl or 'N/A' }} seconds</td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+
+<!-- Modal for Deleting App -->
+<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="staticBackdropLabel">Delete app?</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body">
+                Are you sure you want to delete the app "{{ app.app_id.app_uuid.uuid }}"?
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
+                <a type="button" class="btn btn-danger" href="{{ url_for('qkd_app.delete', app_uuid=app.app_id.app_uuid.uuid) }}">
+                    <i class="bi bi-exclamation-diamond"></i>Yes
+                </a>
+            </div>
+        </div>
+    </div>
+</div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/webui/service/templates/qkd_app/home.html b/src/webui/service/templates/qkd_app/home.html
index 9573013f41410a5d8560e71c174ce6a85237089f..39b43ecc4b9b71e03d27bf080ae6c7be2bf1a90d 100644
--- a/src/webui/service/templates/qkd_app/home.html
+++ b/src/webui/service/templates/qkd_app/home.html
@@ -74,14 +74,12 @@
                         {% endif %}
                     </td>
                     <td>
-                        <!--
-                            <a href="{{ url_for('qkd_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>
-                        -->
+                        <a href="{{ url_for('qkd_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 %}