diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json
index 7ff96dad784fcd70156331ab2d9ce910dac99078..a77d06c185a10f58363cef10f7392eec147d2f09 100644
--- a/gso/oss-params-example.json
+++ b/gso/oss-params-example.json
@@ -48,19 +48,14 @@
   },
   "MONITORING": {
     "LIBRENMS": {
-      "endpoint": "https://librenms.test.gap.geant.org/",
-      "token": "<token>",
-      "DEVICE_GROUPS": {
-        "routers_lab": "lab_routers",
-        "routers_prod": "prod_routers"
-      }
+      "base_url": "https://librenms/api/v0",
+      "token": "<token>"
     },
     "SNMP": {
-      "version": "v2c",
-      "V2": {
-        "community": "librenms-community"
+      "v2c": {
+        "community": "secret-community"
       },
-      "V3": {
+      "v3": {
         "authlevel": "AuthPriv",
         "authname": "librenms",
         "authpass": "<password1>",
diff --git a/gso/services/librenms_client.py b/gso/services/librenms_client.py
index 9a775f3bdfc422714e6cc73d853f76241a44a323..77bd39209d5d02ba7364c8da6684fcd95ad326a4 100644
--- a/gso/services/librenms_client.py
+++ b/gso/services/librenms_client.py
@@ -1,167 +1,107 @@
-"""The LibreNMS module interacts with the LibreNMS instance when
-- Creating a device.
-- Validating the input of a device.
-- Terminating a device.
-"""
-import json
+"""The LibreNMS module interacts with the inventory management system of :term:`GAP`."""
 import logging
+from http import HTTPStatus
+from importlib import metadata
+from typing import Any
 
 import requests
+from requests import HTTPError
 
-from gso import settings
+from gso.settings import load_oss_params
+from gso.utils.helpers import SNMPVersion
 
 logger = logging.getLogger(__name__)
 
 
-class CfgStruct:
-    pass
-
-
-def _get_cfg():
-    """Internal function to retrieve all needed configuration."""
-    oss = settings.load_oss_params()
-    cfg = CfgStruct()
-    # Hack for later ease: 1st setattr will fill in the inner's dict
-    cfg._hack = ""
-    # Update inner dict
-    cfg.__dict__.update(oss.MONITORING)
-    assert cfg.__dict__ is not None
-
-    # Add parameters on-the-fly
-    cfg.headers = {"X-Auth-Token": cfg.LIBRENMS.token}
-
-    sep = "/"
-    if cfg.LIBRENMS.endpoint.endswith("/"):
-        sep = ""
-    cfg.base_url = f"{cfg.LIBRENMS.endpoint}{sep}api/v0"
-    cfg.url_devices = f"{cfg.base_url}/devices"
-    cfg.url_switches = f"{cfg.base_url}/devicegroups/switches"
-    cfg.device_groups = cfg.LIBRENMS.DEVICE_GROUPS
-    cfg.environment = oss.GENERAL.environment
-    if cfg.environment.startswith("lab"):
-        cfg_dg_rtr_lab = cfg.device_groups.routers_lab
-        cfg.url_routers = f"{cfg.base_url}/devicegroups/{cfg_dg_rtr_lab}"
-    elif cfg.environment.startswith("prod"):
-        cfg_dg_rtr_prod = cfg.device_groups.routers_prod
-        cfg.url_routers = f"{cfg.base_url}/devicegroups/{cfg_dg_rtr_prod}"
-
-    return cfg
-
-
-def validate_device(fqdn: str):
-    """Function that validates the existence of a device in LibreNMS.
-
-    :param FQDN of the device to validate.
-    """
-    CFG = _get_cfg()
-
-    # Validate existence
-    nms_result = requests.get(
-        CFG.url_devices, headers=CFG.headers)
-    assert nms_result is not None
-
-    device_id = [x.get("device_id") for x in filter(lambda x: x.get("hostname") == fqdn,
-               nms_result.json().get("devices"))]
-
-    if len(device_id) != 1 or device_id[0] is None:
-        error_msg = f"Device with FQDN={fqdn} is not registered in LibreNMS"
-        print(error_msg)
-        raise AssertionError(error_msg)
-
-    # Validate correctness
-    device_id = device_id[0]
-    url_device = f"{CFG.url_devices}/{device_id}"
-    logger.debug(f"Connecting to URL: {url_device}"
-                 f"with headers: {CFG.headers}")
-    nms_result = requests.get(
-        url_device, headers=CFG.headers)
-    logger.debug(f"LibreNMS response={nms_result.content}")
-
-    if nms_result.status_code != 200:
-        print(nms_result.content)
-        raise AssertionError(nms_result.content)
-
-    # nms_dev_sysname = nms_result.json().get("sysName")
-    nms_dev_hostname = nms_result.json().get("devices")[0].get("hostname")
-    if fqdn != nms_dev_hostname:
-        error_msg = f"Device with FQDN={fqdn} may not be correctly "\
-                f"registered in LibreNMS (expected FQDN: {nms_dev_hostname})"
-        print(error_msg)
-        raise AssertionError(error_msg)
-
-
-def register_device(fqdn: str):
-    """Function that registers a new device in LibreNMS.
-
-    :param FQDN of the device to register.
-    """
-    CFG = _get_cfg()
-    logger.debug(f"Registering FQDN={fqdn} in LibreNMS")
-
-    device_data = {
-        "display": fqdn,
-        "hostname": fqdn,
-        "sysName": fqdn,
-        # "override_icmp_disable": "true",
-        # IMPORTANT: uncomment if testing with FQDNs that are not reachable
-        # from LibreNMS (e.g. ContainerLab routers)
-        # "force_add": "true"
-    }
-    if CFG.SNMP.version == "v2c":
-        device_data.update({
-            "community": CFG.SNMP.V2.community,
-            })
-
-    elif CFG.SNMP.version == "v3":
-        for key in [
-                "authlevel", "authname", "authpass", "authalgo",
-                "cryptopass", "cryptoalgo"]:
-            device_data.update({key: getattr(CFG.SNMP.V3, key)})
-
-    logger.debug(f"Connecting to URL: {CFG.url_devices}"
-                 f"with headers: {CFG.headers} and"
-                 f"payload: {device_data}")
-    nms_result = requests.post(
-        CFG.url_devices, headers=CFG.headers,
-        data=json.dumps(device_data))
-    logger.debug(f"LibreNMS response={nms_result.content}")
-
-    if nms_result.status_code != 200:
-        print(nms_result.content)
-        raise AssertionError(nms_result.content)
-
-
-def deregister_device(fqdn: str):
-    """Function that reregisters a device from LibreNMS.
-
-    :param FQDN of the device to deregister.
-    """
-    CFG = _get_cfg()
-    logger.debug(f"Deregistering FQDN={fqdn} from LibreNMS")
-
-    nms_result = requests.get(
-        CFG.url_devices, headers=CFG.headers)
-    assert nms_result is not None
-    device_id = [x.get("device_id") for x in filter(lambda x: x.get("hostname") == fqdn,
-               nms_result.json().get("devices"))]
-    if len(device_id) != 1:
-        return
-    device_id = device_id[0]
-
-    # https://docs.librenms.org/API/Devices/#endpoint-categories
-    device_data = {
-        "field": "disabled",
-        "data": "1",
-    }
-    url_device = f"{CFG.url_devices}/{device_id}"
-    logger.debug(f"Connecting to URL: {url_device}"
-                 f"with headers: {CFG.headers} and"
-                 f"payload: {device_data}")
-    nms_result = requests.patch(
-        url_device, headers=CFG.headers,
-        data=json.dumps(device_data))
-    logger.debug(f"LibreNMS response={nms_result.content}")
-
-    # Fail silently if device was not registered
-    if nms_result.status_code != 200:
-        print(nms_result.content)
+class LibreNMSClient:
+    """The client for LibreNMS that interacts with the inventory management system."""
+
+    def __init__(self) -> None:
+        """Initialise a new LibreNMS client with an authentication token."""
+        config = load_oss_params().MONITORING
+        token = config.LIBRENMS.token
+
+        self.base_url = config.LIBRENMS.base_url
+        self.snmp_config = config.SNMP
+
+        self.headers = {
+            "User-Agent": f"geant-service-orchestrator/{metadata.version('geant-service-orchestrator')}",
+            "Accept": "application/json",
+            "Content-Type": "application/json",
+            "X-Auth-Token": token,
+        }
+
+    def get_device(self, fqdn: str) -> dict[str, Any]:
+        """Get an existing device from LibreNMS.
+
+        :param str fqdn: The :term:`FQDN` of a device that is retrieved.
+        :return dict[str, Any]: A :term:`JSON` formatted list of devices that match the queried :term:`FQDN`.
+        :raises HTTPError: Raises an HTTP error 404 when the device is not found
+        """
+        response = requests.get(f"{self.base_url}/devices/{fqdn}", headers=self.headers, timeout=(0.5, 75))
+        response.raise_for_status()
+
+        return response.json()
+
+    def device_exists(self, fqdn: str) -> bool:
+        """Check whether a device exists in LibreNMS.
+
+        :param str fqdn: The hostname that should be checked for.
+        :return bool: Whether the device exists or not.
+        """
+        try:
+            device = self.get_device(fqdn)
+        except HTTPError as e:
+            if e.response.status_code == HTTPStatus.NOT_FOUND:
+                return False
+            raise
+
+        return device["status"] == "ok"
+
+    def add_device(self, fqdn: str, snmp_version: SNMPVersion) -> dict[str, Any]:
+        """Add a new device to LibreNMS.
+
+        :param str fqdn: The hostname of the newly added device.
+        :param SNMPVersion snmp_version: The SNMP version of the new device, which decides the authentication parameters
+                                         that LibreNMS should use to poll the device.
+        """
+        device_data = {
+            "display": fqdn,
+            "hostname": fqdn,
+            "sysName": fqdn,
+            "snmpver": snmp_version.value,
+        }
+        device_data.update(getattr(self.snmp_config, snmp_version))
+
+        device = requests.post(f"{self.base_url}/devices", headers=self.headers, json=device_data, timeout=(0.5, 75))
+
+        return device.json()
+
+    def remove_device(self, fqdn: str) -> dict[str, Any]:
+        """Remove a device from LibreNMS.
+
+        :param str fqdn: The :term:`FQDN` of the hostname that should get deleted.
+        :return dict[str, Any]: A JSON representation of the device that got removed.
+        :raises HTTPError: Raises an exception if the request did not succeed.
+        """
+        device = requests.delete(f"{self.base_url}/devices/{fqdn}", headers=self.headers, timeout=(0.5, 75))
+
+        device.raise_for_status()
+        return device.json()
+
+    def validate_device(self, fqdn: str) -> list[str]:
+        """Validate a device in LibreNMS by fetching the record match the queried :term:`FQDN` against its hostname.
+
+        :param str fqdn: The :term:`FQDN` of the host that is validated.
+        :return list[str]: A list of errors, if empty the device is successfully validated.
+        """
+        errors = []
+        device = self.get_device(fqdn)
+
+        if device["status"] != "ok":
+            errors += ["Device does not exist in LibreNMS."]
+
+        if device["hostname"] != fqdn:
+            errors += ["Device hostname in LibreNMS does not match FQDN."]
+
+        return errors
diff --git a/gso/settings.py b/gso/settings.py
index fc59038adf7d78446e12deaf42409416754e3bef..f05632ed3968d8f727c6d108618c0c80a5065ca8 100644
--- a/gso/settings.py
+++ b/gso/settings.py
@@ -94,13 +94,6 @@ class IPAMParams(BaseSettings):
     LT_IAS: ServiceNetworkParams
 
 
-class MonitoringLibreNMSDevGroupsParams(BaseSettings):
-    """Parameters related to LibreNMS device groups."""
-
-    routers_lab: str
-    routers_prod: str
-
-
 class MonitoringSNMPV2Params(BaseSettings):
     """Parameters related to SNMPv2."""
 
@@ -121,16 +114,24 @@ class MonitoringSNMPV3Params(BaseSettings):
 class MonitoringLibreNMSParams(BaseSettings):
     """Parameters related to LibreNMS."""
 
-    endpoint: str
+    base_url: str
     token: str
-    DEVICE_GROUPS: MonitoringLibreNMSDevGroupsParams
+
+
+class SNMPParams(BaseSettings):
+    """Parameters for SNMP in LibreNMS."""
+
+    v2c: MonitoringSNMPV2Params
+    #: .. versionadded :: 2.0
+    #:    Support for :term:`SNMP` v3 will get added in a later version of :term:`GSO`. Parameters are optional for now.
+    v3: MonitoringSNMPV3Params | None
 
 
 class MonitoringParams(BaseSettings):
     """Parameters related to the monitoring."""
 
     LIBRENMS: MonitoringLibreNMSParams
-    SNMP: MonitoringSNMPV2Params | MonitoringSNMPV3Params
+    SNMP: SNMPParams
 
 
 class ProvisioningProxyParams(BaseSettings):
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index 4bcef047fba589b42e332c080574d5ea9995e3b7..9c08bb1533a8badd0b87109b3d10977c2433ce3d 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -2,6 +2,7 @@
 
 import ipaddress
 import re
+from enum import StrEnum
 from ipaddress import IPv4Address
 from uuid import UUID
 
@@ -30,6 +31,13 @@ class LAGMember(BaseModel):
         return hash((self.interface_name, self.interface_description))
 
 
+class SNMPVersion(StrEnum):
+    """An enumerator for the two relevant versions of :term:`SNMP`: v2c and 3."""
+
+    V2C = "v2c"
+    V3 = "v3"
+
+
 def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None:
     """Return a list of available interfaces for a given router and speed.