diff --git a/.gitignore b/.gitignore
index 1dbcfe83d7e32a2d92dfb1f8bfcd81a91b5a27bc..e1b97795eb689949283a4511d2f04f7617cd97e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 .env
-log/
+/log/
 go.work
 go.work.sum
-database.db
-plugins/
\ No newline at end of file
+database.db*
+/plugins/
+/backend
+/backend.exe
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c19d2cca03f729e14172ed1a5ffb74d3f3eb60db..43fc472915a8640e7ca8fae8e74ac3b813c0368e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,7 +26,7 @@ check-if-api-docs-updated:
   before_script:
   - go install github.com/swaggo/swag/cmd/swag@latest
   script:
-  - $GOPATH/bin/swag init
+  - $GOPATH/bin/swag init --parseDependency
   - |
     if ! git diff --quiet; then
       echo "The API docs needs to be updated first"
diff --git a/Readme.md b/Readme.md
index a294be903d82a6d3d380664f75e5da59398f74cc..3710da44533210361d939e642325c5553ee07e03 100644
--- a/Readme.md
+++ b/Readme.md
@@ -16,5 +16,6 @@ The configuration can either be done through environment variables or a file cal
 | EXIT_ON_DISCONNECT | `false` | Exit the application on MQTT disconnect |
 | Z2M_BASETOPIC | `zigbee2mqtt` | Base topic for ZigBee2MQTT |
 | DB_DRIVER | `sqlite` | Database driver, currently ony sqlite is supported |
-| DB_NAME | `./database.db` | Name of the databse, for sqlite path to the DB file
-| EXECUTE_MIGRATIONS | `true` | Execute database migrations on start |
\ No newline at end of file
+| DB_NAME | `./database.db` | Name of the databse, for sqlite path to the DB file |
+| EXECUTE_MIGRATIONS | `true` | Execute database migrations on start |
+| PLUGIN_DIR | `./plugins` | Where to search for plugins |
diff --git a/alarm/alarm.go b/alarm/alarm.go
new file mode 100644
index 0000000000000000000000000000000000000000..627a201ace179c02b58f4892992a6263f4ce54ca
--- /dev/null
+++ b/alarm/alarm.go
@@ -0,0 +1,31 @@
+package alarm
+
+import (
+	"slices"
+
+	"jonasled.dev/firehouse-smokedetection/backend/database/tables"
+	"jonasled.dev/firehouse-smokedetection/backend/types"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+	"jonasled.dev/jonasled/go-libs/log"
+)
+
+func Alarm(mqttMessage types.Z2MSmoke, smokeDetector tables.SmokeDetector) {
+	log.Log.Infof("Sensor %s reported smoke", smokeDetector.Name)
+	if smokeDetector.AlarmPlugin == "" {
+		return
+	}
+	if !slices.Contains(GetAllPlugins(), smokeDetector.AlarmPlugin) {
+		log.Log.Errorf("Plugin %s for smoke sensor %s not found, can't forward alarm", smokeDetector.AlarmPlugin, smokeDetector.Name)
+		return
+	}
+	err := Plugins[smokeDetector.AlarmPlugin].Alarm(plugintypes.AlarmSmokeSensor{
+		Smoke:      mqttMessage.Smoke,
+		Test:       mqttMessage.Test,
+		Name:       smokeDetector.Name,
+		ZigBeeName: smokeDetector.ZigBeeName,
+		Tamper:     mqttMessage.Tamper,
+	}, smokeDetector.AlarmPluginConfigId)
+	if err != nil {
+		log.Log.Error("Failed executing smoke alarm: ", err.Error())
+	}
+}
diff --git a/alarm/getPlugins.go b/alarm/getPlugins.go
new file mode 100644
index 0000000000000000000000000000000000000000..865295a08c9d28c79d49c74a92e0e5d4748ba2ec
--- /dev/null
+++ b/alarm/getPlugins.go
@@ -0,0 +1,9 @@
+package alarm
+
+func GetAllPlugins() []string {
+	var result []string
+	for key := range Plugins {
+		result = append(result, key)
+	}
+	return result
+}
diff --git a/alarm/init.go b/alarm/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..a4db94c13574ee7698fac89887d15f91b15b6bdb
--- /dev/null
+++ b/alarm/init.go
@@ -0,0 +1,54 @@
+package alarm
+
+import (
+	"os"
+	"path/filepath"
+	"plugin"
+	"strings"
+
+	plugininterface "jonasled.dev/firehouse-smokedetection/plugin-interface"
+	"jonasled.dev/jonasled/go-libs/config"
+	"jonasled.dev/jonasled/go-libs/log"
+)
+
+var Plugins map[string]plugininterface.Plugin
+
+func Init() {
+	Plugins = make(map[string]plugininterface.Plugin)
+	err := filepath.Walk(config.GetOrDefault("PLUGIN_DIR", "./plugins"), func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			log.Log.Fatal("Failed reading plugin dir: ", err.Error())
+		}
+
+		if !info.IsDir() && strings.HasSuffix(info.Name(), ".so") {
+			log.Log.Debug("Found plugin file at ", path)
+			plg, err := plugin.Open(path)
+			if err != nil {
+				log.Log.Fatalf("Failed reading Plugin %s: %s", path, err.Error())
+			}
+			v, err := plg.Lookup("Plugin")
+			if err != nil {
+				log.Log.Fatalf("Plugin %s doesn't export Plugin var", path)
+			}
+			plugin, ok := v.(plugininterface.Plugin)
+			if !ok {
+				log.Log.Fatalf("Failed parsing plugin %s as Plugin Interface", path)
+			}
+
+			pluginConfig := PluginConfig{
+				PluginName: plugin.GetMetadata().Name,
+			}
+
+			plugin.Setup(&pluginConfig)
+
+			log.Log.Infof("Initialized Plugin %s (v%s)", plugin.GetMetadata().Name, plugin.GetMetadata().Version)
+
+			Plugins[plugin.GetMetadata().Name] = plugin
+		}
+		return nil
+	})
+
+	if err != nil {
+		log.Log.Fatal("Failed reading plugin dir: ", err.Error())
+	}
+}
diff --git a/alarm/pluginConfig.go b/alarm/pluginConfig.go
new file mode 100644
index 0000000000000000000000000000000000000000..76ff2c5eab565bdada6ecacaa563c212056ec65c
--- /dev/null
+++ b/alarm/pluginConfig.go
@@ -0,0 +1,33 @@
+package alarm
+
+import (
+	"jonasled.dev/firehouse-smokedetection/backend/database"
+	"jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+)
+
+type PluginConfig struct {
+	PluginName string
+}
+
+func (p *PluginConfig) GetConfig(id uint) types.AlarmConfig {
+	var alarmConfig types.AlarmConfig
+	database.Db.Where(types.AlarmConfig{ID: id, Plugin: p.PluginName}).First(&alarmConfig)
+
+	return alarmConfig
+}
+
+func (p *PluginConfig) SetConfig(config types.AlarmConfig) error {
+	config.Plugin = p.PluginName
+	return database.Db.Save(&config).Error
+}
+
+func (p *PluginConfig) DeleteConfig(config types.AlarmConfig) error {
+	config.Plugin = p.PluginName
+	return database.Db.Delete(&config).Error
+}
+
+func (p *PluginConfig) ListConfig(filter types.AlarmConfig) []types.AlarmConfig {
+	var matchedConfigs []types.AlarmConfig
+	database.Db.Find(&matchedConfigs, &filter)
+	return matchedConfigs
+}
diff --git a/database/gorm.go b/database/gorm.go
index 4a6517a9fd2e44cd88ef481e48c05507f548e888..e377bd3a94440cc60c865f4e4653379f437e5a8d 100644
--- a/database/gorm.go
+++ b/database/gorm.go
@@ -9,6 +9,7 @@ import (
 	"gorm.io/gorm"
 	"gorm.io/gorm/logger"
 	"jonasled.dev/firehouse-smokedetection/backend/database/tables"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
 	"jonasled.dev/jonasled/go-libs/config"
 	"jonasled.dev/jonasled/go-libs/log"
 )
@@ -40,6 +41,6 @@ func Init() {
 
 	if config.GetOrDefault("EXECUTE_MIGRATIONS", "true") == "true" {
 		log.Log.Info("Executing database migrations")
-		Db.AutoMigrate(&tables.SmokeDetector{})
+		Db.AutoMigrate(&tables.SmokeDetector{}, &plugintypes.AlarmConfig{})
 	}
 }
diff --git a/database/tables/smokeDetector.go b/database/tables/smokeDetector.go
index e0b04603dee2ae4f713ba5b706bf523e38b5fd85..45de9640b7ddca8251f6cc9c4b118b498b3f4cfa 100644
--- a/database/tables/smokeDetector.go
+++ b/database/tables/smokeDetector.go
@@ -11,4 +11,6 @@ type SmokeDetector struct {
 	ZigBeeName string `gorm:"unique"`
 	Name       string
 	Battery    int
+	AlarmPlugin string
+	AlarmPluginConfigId uint
 }
diff --git a/docs/docs.go b/docs/docs.go
index bd122488b16e15281a99029311cbe95f6c6caf88..fc1aade0c92ff0008ff6af3e2b5a44b33fc01131 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -135,6 +135,170 @@ const docTemplate = `{
                 }
             }
         },
+        "/api/v1/plugins": {
+            "get": {
+                "description": "List all registered plugins",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "List plugins",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.PluginMetadata"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "/api/v1/plugins/{plugin}": {
+            "post": {
+                "description": "Creates a new config element for the given plugin",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Create Config",
+                "parameters": [
+                    {
+                        "description": "New config entry for plugin",
+                        "name": "config",
+                        "in": "body",
+                        "required": true,
+                        "schema": {}
+                    },
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
+        "/api/v1/plugins/{plugin}/schema": {
+            "get": {
+                "description": "Returns the plugins config JSON schema",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Plugin config schema",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
+        "/api/v1/plugins/{plugin}/{configId}": {
+            "get": {
+                "description": "Get a existing config",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Get Config",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            },
+            "put": {
+                "description": "Updates a existing config element for the given plugin",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Update Config",
+                "parameters": [
+                    {
+                        "description": "New config entry for plugin",
+                        "name": "config",
+                        "in": "body",
+                        "required": true,
+                        "schema": {}
+                    },
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            },
+            "delete": {
+                "description": "Delete a existing config",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Delete Config",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
         "/api/v1/smokedetectors": {
             "get": {
                 "description": "List all known smoke detectors",
@@ -291,6 +455,12 @@ const docTemplate = `{
         "tables.SmokeDetector": {
             "type": "object",
             "properties": {
+                "alarmPlugin": {
+                    "type": "string"
+                },
+                "alarmPluginConfigId": {
+                    "type": "integer"
+                },
                 "battery": {
                     "type": "integer"
                 },
@@ -341,9 +511,26 @@ const docTemplate = `{
                 }
             }
         },
+        "types.PluginMetadata": {
+            "type": "object",
+            "properties": {
+                "name": {
+                    "type": "string"
+                },
+                "version": {
+                    "type": "string"
+                }
+            }
+        },
         "types.UpdateSmokeDetectorRequest": {
             "type": "object",
             "properties": {
+                "alarmPlugin": {
+                    "type": "string"
+                },
+                "alarmPluginConfigId": {
+                    "type": "integer"
+                },
                 "name": {
                     "type": "string"
                 }
diff --git a/docs/swagger.json b/docs/swagger.json
index 266c47510140916b77892fbcf4dc94bb5216ca3e..51044b5dfbb6f3c9cf5e2a11b02efd0b52a201fc 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -126,6 +126,170 @@
                 }
             }
         },
+        "/api/v1/plugins": {
+            "get": {
+                "description": "List all registered plugins",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "List plugins",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.PluginMetadata"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "/api/v1/plugins/{plugin}": {
+            "post": {
+                "description": "Creates a new config element for the given plugin",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Create Config",
+                "parameters": [
+                    {
+                        "description": "New config entry for plugin",
+                        "name": "config",
+                        "in": "body",
+                        "required": true,
+                        "schema": {}
+                    },
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
+        "/api/v1/plugins/{plugin}/schema": {
+            "get": {
+                "description": "Returns the plugins config JSON schema",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Plugin config schema",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
+        "/api/v1/plugins/{plugin}/{configId}": {
+            "get": {
+                "description": "Get a existing config",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Get Config",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            },
+            "put": {
+                "description": "Updates a existing config element for the given plugin",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Update Config",
+                "parameters": [
+                    {
+                        "description": "New config entry for plugin",
+                        "name": "config",
+                        "in": "body",
+                        "required": true,
+                        "schema": {}
+                    },
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            },
+            "delete": {
+                "description": "Delete a existing config",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "plugins"
+                ],
+                "summary": "Delete Config",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Plugin name",
+                        "name": "plugin",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "ID of the config to update",
+                        "name": "configId",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {}
+            }
+        },
         "/api/v1/smokedetectors": {
             "get": {
                 "description": "List all known smoke detectors",
@@ -282,6 +446,12 @@
         "tables.SmokeDetector": {
             "type": "object",
             "properties": {
+                "alarmPlugin": {
+                    "type": "string"
+                },
+                "alarmPluginConfigId": {
+                    "type": "integer"
+                },
                 "battery": {
                     "type": "integer"
                 },
@@ -332,9 +502,26 @@
                 }
             }
         },
+        "types.PluginMetadata": {
+            "type": "object",
+            "properties": {
+                "name": {
+                    "type": "string"
+                },
+                "version": {
+                    "type": "string"
+                }
+            }
+        },
         "types.UpdateSmokeDetectorRequest": {
             "type": "object",
             "properties": {
+                "alarmPlugin": {
+                    "type": "string"
+                },
+                "alarmPluginConfigId": {
+                    "type": "integer"
+                },
                 "name": {
                     "type": "string"
                 }
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index f2141cdae725c3dc1f7ae489307b85120e2f3507..8e1f810995f183d4a4a46b532b440a896e301ac4 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -1,6 +1,10 @@
 definitions:
   tables.SmokeDetector:
     properties:
+      alarmPlugin:
+        type: string
+      alarmPluginConfigId:
+        type: integer
       battery:
         type: integer
       createdAt:
@@ -33,8 +37,19 @@ definitions:
       end:
         type: string
     type: object
+  types.PluginMetadata:
+    properties:
+      name:
+        type: string
+      version:
+        type: string
+    type: object
   types.UpdateSmokeDetectorRequest:
     properties:
+      alarmPlugin:
+        type: string
+      alarmPluginConfigId:
+        type: integer
       name:
         type: string
     type: object
@@ -122,6 +137,119 @@ paths:
       summary: Enable pairing
       tags:
       - pairing
+  /api/v1/plugins:
+    get:
+      description: List all registered plugins
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              $ref: '#/definitions/types.PluginMetadata'
+            type: array
+      summary: List plugins
+      tags:
+      - plugins
+  /api/v1/plugins/{plugin}:
+    post:
+      description: Creates a new config element for the given plugin
+      parameters:
+      - description: New config entry for plugin
+        in: body
+        name: config
+        required: true
+        schema: {}
+      - description: Plugin name
+        in: path
+        name: plugin
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses: {}
+      summary: Create Config
+      tags:
+      - plugins
+  /api/v1/plugins/{plugin}/{configId}:
+    delete:
+      description: Delete a existing config
+      parameters:
+      - description: Plugin name
+        in: path
+        name: plugin
+        required: true
+        type: string
+      - description: ID of the config to update
+        in: path
+        name: configId
+        required: true
+        type: integer
+      produces:
+      - application/json
+      responses: {}
+      summary: Delete Config
+      tags:
+      - plugins
+    get:
+      description: Get a existing config
+      parameters:
+      - description: Plugin name
+        in: path
+        name: plugin
+        required: true
+        type: string
+      - description: ID of the config to update
+        in: path
+        name: configId
+        required: true
+        type: integer
+      produces:
+      - application/json
+      responses: {}
+      summary: Get Config
+      tags:
+      - plugins
+    put:
+      description: Updates a existing config element for the given plugin
+      parameters:
+      - description: New config entry for plugin
+        in: body
+        name: config
+        required: true
+        schema: {}
+      - description: Plugin name
+        in: path
+        name: plugin
+        required: true
+        type: string
+      - description: ID of the config to update
+        in: path
+        name: configId
+        required: true
+        type: integer
+      produces:
+      - application/json
+      responses: {}
+      summary: Update Config
+      tags:
+      - plugins
+  /api/v1/plugins/{plugin}/schema:
+    get:
+      description: Returns the plugins config JSON schema
+      parameters:
+      - description: Plugin name
+        in: path
+        name: plugin
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses: {}
+      summary: Plugin config schema
+      tags:
+      - plugins
   /api/v1/smokedetectors:
     get:
       description: List all known smoke detectors
diff --git a/go.mod b/go.mod
index 55de059b51d9b3f468e9763c6f98b16213335d77..a81ac27e1660a778201b864f9bbc8d64cd1934d7 100644
--- a/go.mod
+++ b/go.mod
@@ -6,16 +6,17 @@ require (
 	github.com/eclipse/paho.mqtt.golang v1.5.0
 	github.com/gin-gonic/gin v1.10.0
 	github.com/glebarez/sqlite v1.11.0
+	github.com/invopop/jsonschema v0.13.0
 	github.com/joho/godotenv v1.5.1
 	github.com/nekomeowww/gorm-logger-logrus v1.0.8
 	github.com/sirupsen/logrus v1.9.3
 	github.com/swaggo/files v1.0.1
-	github.com/swaggo/files/v2 v2.0.2
 	github.com/swaggo/gin-swagger v1.6.0
 	github.com/swaggo/swag v1.16.4
 	github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f
 	github.com/wind-c/comqtt/v2 v2.6.0
 	gorm.io/gorm v1.25.12
+	jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14
 	jonasled.dev/jonasled/go-libs v0.0.3
 )
 
@@ -23,6 +24,8 @@ require (
 	github.com/KyleBanks/depth v1.2.1 // indirect
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
+	github.com/bahlo/generic-list-go v0.2.0 // indirect
+	github.com/buger/jsonparser v1.1.1 // indirect
 	github.com/bytedance/sonic v1.11.6 // indirect
 	github.com/bytedance/sonic/loader v0.1.1 // indirect
 	github.com/cloudwego/base64x v0.1.4 // indirect
@@ -47,7 +50,7 @@ require (
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
-	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -56,12 +59,13 @@ require (
 	github.com/rs/xid v1.5.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
+	github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
 	golang.org/x/arch v0.8.0 // indirect
 	golang.org/x/crypto v0.25.0 // indirect
 	golang.org/x/net v0.27.0 // indirect
-	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
 	golang.org/x/sys v0.22.0 // indirect
-	golang.org/x/text v0.16.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 	golang.org/x/tools v0.23.0 // indirect
 	google.golang.org/protobuf v1.34.1 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
diff --git a/go.sum b/go.sum
index 31ecab3477b10045a68ff0d3cbc35665dd639560..77de2d78e8339ffebce8eb16e32a7e4248e0321e 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,10 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
+github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
+github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
+github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -61,6 +65,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
+github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
 github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
 github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -86,8 +92,9 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -125,7 +132,6 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
 github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
-github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
 github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
 github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
 github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
@@ -138,6 +144,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
 github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/wind-c/comqtt/v2 v2.6.0 h1:huLdOwYDrwMTrEwH7+mSs1GftHZ/tDqJw8nOz3iX7kc=
 github.com/wind-c/comqtt/v2 v2.6.0/go.mod h1:6O4VilBrTQ/cNIcmIgNdMLCK9DTiLRxkal00t0DLN64=
+github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
+github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
@@ -158,8 +166,8 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
 golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -179,8 +187,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -204,8 +212,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
 gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
-jonasled.dev/jonasled/go-libs v0.0.2 h1:QJE0jy9SH8flqAJIJHMG6NE3VPZurCvvMGf5xN0C+Xc=
-jonasled.dev/jonasled/go-libs v0.0.2/go.mod h1:E8Sev4obR0x6y7x0HaIN1tImeLnVJgwJXt++jzTwiFM=
+jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14 h1:3HxNjEiZbQuIPSxdlnffKIqnYsjgtvgerN/4r4WOjmk=
+jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14/go.mod h1:7OmpERugGg+B1OKE5R2glhdbbF2XmYP0OTfbdNNBm40=
 jonasled.dev/jonasled/go-libs v0.0.3 h1:2Tg1pYJMufBMPkyYVVAvsUJe/R+TRRtRVW9MCbc24bM=
 jonasled.dev/jonasled/go-libs v0.0.3/go.mod h1:E8Sev4obR0x6y7x0HaIN1tImeLnVJgwJXt++jzTwiFM=
 modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
diff --git a/main.go b/main.go
index 61c5cb0cb9a0184569a755f1fc2bb022e4ba0f5f..04d33347147418ad02d4cdf924e282746bc2a9e0 100644
--- a/main.go
+++ b/main.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	_ "github.com/joho/godotenv/autoload"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
 	"jonasled.dev/firehouse-smokedetection/backend/database"
 	"jonasled.dev/firehouse-smokedetection/backend/mqttclient"
 	"jonasled.dev/firehouse-smokedetection/backend/mqttserver"
@@ -26,6 +27,7 @@ func main() {
 	database.Init()
 	mqttclient.Init()
 	webserver.Init()
+	alarm.Init()
 
 	webserver.Run()
 }
diff --git a/mqttclient/mqttmessage.go b/mqttclient/mqttmessage.go
index 496920bfcb64507adb50eda917177ad9d8b09350..75e7721bb3935aaedae945aaec934bba5f79efc3 100644
--- a/mqttclient/mqttmessage.go
+++ b/mqttclient/mqttmessage.go
@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	mqtt "github.com/eclipse/paho.mqtt.golang"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
 	"jonasled.dev/firehouse-smokedetection/backend/cache"
 	"jonasled.dev/firehouse-smokedetection/backend/database"
 	"jonasled.dev/firehouse-smokedetection/backend/database/tables"
@@ -35,8 +36,8 @@ var messagePubHandlerZ2M mqtt.MessageHandler = func(client mqtt.Client, msg mqtt
 	var smokeDetector tables.SmokeDetector
 	topicSplit := strings.Split(msg.Topic(), "/")
 	deviceName := topicSplit[len(topicSplit)-1]
-	tx := database.Db.First(&smokeDetector, tables.SmokeDetector{ZigBeeName: deviceName})
-	if tx.RowsAffected == 0 {
+	database.Db.First(&smokeDetector, tables.SmokeDetector{ZigBeeName: deviceName})
+	if smokeDetector.ID == 0 {
 		log.Log.Info("Found new smoke detector: ", deviceName)
 		smokeDetector = tables.SmokeDetector{
 			ZigBeeName: deviceName,
@@ -47,7 +48,7 @@ var messagePubHandlerZ2M mqtt.MessageHandler = func(client mqtt.Client, msg mqtt
 	database.Db.Save(&smokeDetector)
 
 	if mqttMessage.Smoke {
-		log.Log.Infof("Sensor %s reported smoke", smokeDetector.Name)
+		alarm.Alarm(mqttMessage, smokeDetector)
 	}
 
 }
diff --git a/types/updateSmokeDetectorRequest.go b/types/updateSmokeDetectorRequest.go
index c7c96e41da771c8f17060b79d3d26d1fd82c069f..286ce16855c358b98896d361ffe6766cdd0529c5 100644
--- a/types/updateSmokeDetectorRequest.go
+++ b/types/updateSmokeDetectorRequest.go
@@ -1,5 +1,7 @@
 package types
 
 type UpdateSmokeDetectorRequest struct {
-	Name string `json:"name"`
+	Name                string `json:"name"`
+	AlarmPlugin         string `json:"alarmPlugin"`
+	AlarmPluginConfigId uint   `json:"alarmPluginConfigId"`
 }
diff --git a/webserver/plugins/deleteConfig.go b/webserver/plugins/deleteConfig.go
new file mode 100644
index 0000000000000000000000000000000000000000..a3a609eb33c99ad4324f865e0cf329236fe253e9
--- /dev/null
+++ b/webserver/plugins/deleteConfig.go
@@ -0,0 +1,47 @@
+package plugins
+
+import (
+	"net/http"
+	"slices"
+	"strconv"
+
+	"github.com/gin-gonic/gin"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
+	"jonasled.dev/firehouse-smokedetection/backend/database"
+	"jonasled.dev/firehouse-smokedetection/backend/types"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+)
+
+// @Summary		Delete Config
+// @Description	Delete a existing config
+// @Tags			plugins
+// @Produce		json
+// @Param			plugin		path	string	true	"Plugin name"
+// @Param			configId	path	int		true	"ID of the config to update"
+// @router			/api/v1/plugins/{plugin}/{configId} [delete]
+func deleteConfig(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginName := ctx.Param("plugin")
+	configId := ctx.Param("id")
+	if !slices.Contains(pluginNames, pluginName) {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Plugin with given name not found",
+		})
+		return
+	}
+
+	configIdInt, err := strconv.Atoi(configId)
+	if err != nil {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Failed parsing config id as number",
+		})
+		return
+	}
+	database.Db.Delete(plugintypes.AlarmConfig{ID: uint(configIdInt), Plugin: pluginName})
+	ctx.JSON(http.StatusOK, types.GeneralResponse{
+		Success: true,
+		Message: "The config has been deleted",
+	})
+}
diff --git a/webserver/plugins/getConfig.go b/webserver/plugins/getConfig.go
new file mode 100644
index 0000000000000000000000000000000000000000..3166b78241b3ae6d31761624dbfa63f82d90f6e1
--- /dev/null
+++ b/webserver/plugins/getConfig.go
@@ -0,0 +1,46 @@
+package plugins
+
+import (
+	"net/http"
+	"slices"
+	"strconv"
+
+	"github.com/gin-gonic/gin"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
+	"jonasled.dev/firehouse-smokedetection/backend/database"
+	"jonasled.dev/firehouse-smokedetection/backend/types"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+)
+
+// @Summary		Get Config
+// @Description	Get a existing config
+// @Tags			plugins
+// @Produce		json
+// @Param			plugin		path	string	true	"Plugin name"
+// @Param			configId	path	int		true	"ID of the config to update"
+// @router			/api/v1/plugins/{plugin}/{configId} [get]
+func getConfig(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginName := ctx.Param("plugin")
+	configId := ctx.Param("id")
+	if !slices.Contains(pluginNames, pluginName) {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Plugin with given name not found",
+		})
+		return
+	}
+
+	configIdInt, err := strconv.Atoi(configId)
+	if err != nil {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Failed parsing config id as number",
+		})
+		return
+	}
+	var alarmConfig plugintypes.AlarmConfig
+	database.Db.Where(plugintypes.AlarmConfig{ID: uint(configIdInt), Plugin: pluginName}).First(&alarmConfig)
+	ctx.Header("Content-Type", "application/json")
+	ctx.String(http.StatusOK, alarmConfig.Config)
+}
diff --git a/webserver/plugins/getSchema.go b/webserver/plugins/getSchema.go
new file mode 100644
index 0000000000000000000000000000000000000000..17bf76650f50b65e225572f37d1aa037571b9d5a
--- /dev/null
+++ b/webserver/plugins/getSchema.go
@@ -0,0 +1,32 @@
+package plugins
+
+import (
+	"net/http"
+	"slices"
+
+	"github.com/gin-gonic/gin"
+	"github.com/invopop/jsonschema"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
+	"jonasled.dev/firehouse-smokedetection/backend/types"
+)
+
+//	@Summary		Plugin config schema
+//	@Description	Returns the plugins config JSON schema
+//	@Tags			plugins
+//	@Produce		json
+//	@Param			plugin	path	string	true	"Plugin name"
+//	@router			/api/v1/plugins/{plugin}/schema [get]
+func getSchema(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginName := ctx.Param("plugin")
+	if !slices.Contains(pluginNames, pluginName) {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Plugin with given name not found",
+		})
+		return
+	}
+
+	jsonSchema := jsonschema.Reflect(alarm.Plugins[pluginName].GetConfigType())
+	ctx.JSON(http.StatusOK, jsonSchema)
+}
diff --git a/webserver/plugins/init.go b/webserver/plugins/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..433fe1e884b97ece3d97885c1a557d7bf8aaddbe
--- /dev/null
+++ b/webserver/plugins/init.go
@@ -0,0 +1,16 @@
+package plugins
+
+import "github.com/gin-gonic/gin"
+
+func Init(r *gin.Engine) {
+	plugins := r.Group("/api/v1/plugins")
+	{
+		plugins.GET("", listAll)
+		plugins.GET(":plugin/schema", getSchema)
+		plugins.POST(":plugin", setNewConfig)
+		plugins.PUT(":plugin/:id", updateConfig)
+		plugins.GET(":plugin/:id", getConfig)
+		plugins.DELETE(":plugin/:id", deleteConfig)
+	}
+
+}
diff --git a/webserver/plugins/list.go b/webserver/plugins/list.go
new file mode 100644
index 0000000000000000000000000000000000000000..d9dafe9965f24f7a175b647819ed087914f6ff53
--- /dev/null
+++ b/webserver/plugins/list.go
@@ -0,0 +1,26 @@
+package plugins
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+)
+
+// @Summary		List plugins
+// @Description	List all registered plugins
+// @Tags			plugins
+// @Produce		json
+// @Success		200	{object}	[]plugintypes.PluginMetadata
+// @router			/api/v1/plugins [get]
+func listAll(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginResponse := []plugintypes.PluginMetadata{}
+
+	for _, pluginName := range pluginNames {
+		pluginResponse = append(pluginResponse, alarm.Plugins[pluginName].GetMetadata())
+	}
+
+	ctx.JSON(http.StatusOK, pluginResponse)
+}
diff --git a/webserver/plugins/setConfig.go b/webserver/plugins/setConfig.go
new file mode 100644
index 0000000000000000000000000000000000000000..deb62947e2ea2fedae18971c1f7f09a604e0d41a
--- /dev/null
+++ b/webserver/plugins/setConfig.go
@@ -0,0 +1,109 @@
+package plugins
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"slices"
+	"strconv"
+
+	"github.com/gin-gonic/gin"
+	"jonasled.dev/firehouse-smokedetection/backend/alarm"
+	"jonasled.dev/firehouse-smokedetection/backend/database"
+	"jonasled.dev/firehouse-smokedetection/backend/types"
+	plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types"
+)
+
+// @Summary		Create Config
+// @Description	Creates a new config element for the given plugin
+// @Tags			plugins
+// @Produce		json
+// @Param			config	body	interface{}	true	"New config entry for plugin"
+// @Param			plugin	path	string		true	"Plugin name"
+// @router			/api/v1/plugins/{plugin} [post]
+func setNewConfig(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginName := ctx.Param("plugin")
+	if !slices.Contains(pluginNames, pluginName) {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Plugin with given name not found",
+		})
+		return
+	}
+
+	body, _ := io.ReadAll(ctx.Request.Body)
+	pluginConfig := alarm.Plugins[pluginName].GetConfigType()
+	err := json.Unmarshal(body, &pluginConfig)
+	if err != nil {
+		ctx.JSON(http.StatusBadRequest, types.GeneralResponse{
+			Success: true,
+			Message: "failed parsing configuration: " + err.Error(),
+		})
+		return
+	}
+	configJson, _ := json.Marshal(&pluginConfig)
+	dbPluginConfig := plugintypes.AlarmConfig{
+		Plugin: pluginName,
+		Config: string(configJson),
+	}
+	database.Db.Save(&dbPluginConfig)
+
+	ctx.JSON(http.StatusOK, types.GeneralResponse{
+		Success: true,
+		Message: "The config has been created",
+	})
+}
+
+// @Summary		Update Config
+// @Description	Updates a existing config element for the given plugin
+// @Tags			plugins
+// @Produce		json
+// @Param			config		body	interface{}	true	"New config entry for plugin"
+// @Param			plugin		path	string		true	"Plugin name"
+// @Param			configId	path	int			true	"ID of the config to update"
+// @router			/api/v1/plugins/{plugin}/{configId} [put]
+func updateConfig(ctx *gin.Context) {
+	pluginNames := alarm.GetAllPlugins()
+	pluginName := ctx.Param("plugin")
+	configId := ctx.Param("id")
+	if !slices.Contains(pluginNames, pluginName) {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Plugin with given name not found",
+		})
+		return
+	}
+
+	configIdInt, err := strconv.Atoi(configId)
+	if err != nil {
+		ctx.JSON(http.StatusNotFound, types.GeneralResponse{
+			Success: false,
+			Message: "Failed parsing config id as number",
+		})
+		return
+	}
+
+	body, _ := io.ReadAll(ctx.Request.Body)
+	pluginConfig := alarm.Plugins[pluginName].GetConfigType()
+	err = json.Unmarshal(body, &pluginConfig)
+	if err != nil {
+		ctx.JSON(http.StatusBadRequest, types.GeneralResponse{
+			Success: true,
+			Message: "failed parsing configuration: " + err.Error(),
+		})
+		return
+	}
+	configJson, _ := json.Marshal(&pluginConfig)
+	dbPluginConfig := plugintypes.AlarmConfig{
+		Plugin: pluginName,
+		ID:     uint(configIdInt),
+		Config: string(configJson),
+	}
+	database.Db.Save(&dbPluginConfig)
+
+	ctx.JSON(http.StatusOK, types.GeneralResponse{
+		Success: true,
+		Message: "The config has been updated",
+	})
+}
diff --git a/webserver/smokeDetectors/update.go b/webserver/smokeDetectors/update.go
index 4a529dc53737995ff4f296372d463bfb16d25be8..ad695da0ebb6bc658c56866f7ad0e78cdede2d36 100644
--- a/webserver/smokeDetectors/update.go
+++ b/webserver/smokeDetectors/update.go
@@ -53,6 +53,13 @@ func update(ctx *gin.Context) {
 	if requestBody.Name != "" {
 		smokeDetectors.Name = requestBody.Name
 	}
+	if requestBody.AlarmPlugin != "" {
+		smokeDetectors.AlarmPlugin = requestBody.AlarmPlugin
+	}
+	if requestBody.AlarmPluginConfigId != 0 {
+		smokeDetectors.AlarmPluginConfigId = requestBody.AlarmPluginConfigId
+	}
+
 	database.Db.Save(&smokeDetectors)
 
 	ctx.JSON(http.StatusOK, &smokeDetectors)
diff --git a/webserver/webserver.go b/webserver/webserver.go
index 2c2f33b68dbfdfa53b462abc366d2f13d8d4131c..e1efd97330aae9481a5757d1aa4c28abfbb3f445 100644
--- a/webserver/webserver.go
+++ b/webserver/webserver.go
@@ -4,6 +4,7 @@ import (
 	"github.com/gin-gonic/gin"
 	ginlogrus "github.com/toorop/gin-logrus"
 	"jonasled.dev/firehouse-smokedetection/backend/webserver/mqtt"
+	"jonasled.dev/firehouse-smokedetection/backend/webserver/plugins"
 	"jonasled.dev/firehouse-smokedetection/backend/webserver/smokeDetectors"
 	"jonasled.dev/jonasled/go-libs/log"
 )
@@ -18,6 +19,7 @@ func Init() {
 	initSwaggerRoutes()
 	mqtt.Init(R)
 	smokeDetectors.Init(R)
+	plugins.Init(R)
 
 }