diff --git a/.env b/.env
index 14e070594156cefa334ce3410afd074b34000c0c..f9c934b3c494623cc8b704f3e1cb31e9446e6938 100644
--- a/.env
+++ b/.env
@@ -34,3 +34,10 @@ CERN_OIDC_CLIENT_SECRET=fill-me
 #EMAIL_BACKEND=vendor.django_mail.backends.smimesmtp.EmailBackend
 #EMAIL_SMIME_CERT_FILE_PATH=/etc/notifications-noreply.pem
 #EMAIL_SMIME_CERT_KEY_FILE_PATH=/etc/notifications-noreply-key.pem
+
+# Auditing
+# ETCD_HOST=localhost
+# ETCD_PORT=2379
+# AUDITING=true
+# ETCD_USER=audituser
+# ETCD_PASSWORD=fill-me
\ No newline at end of file
diff --git a/.isort.cfg b/.isort.cfg
index 918f3dd28c505839262a666e62be15db69e29191..310e5588152be10113c0167b9061ab357d58fa4d 100644
--- a/.isort.cfg
+++ b/.isort.cfg
@@ -4,4 +4,4 @@ multi_line_output=3
 include_trailing_comma=True
 lines_after_imports=2
 not_skip=__init__.py
-known_third_party = Crypto,apns2,cachetools,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml
+known_third_party = Crypto,apns2,cachetools,etcd3,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml
diff --git a/Makefile b/Makefile
index bb11a9ecbb186d05fbdda4a446f8c7e741ff6d83..72d29c95e7e35825594365511eaccde9df569c8f 100644
--- a/Makefile
+++ b/Makefile
@@ -56,6 +56,7 @@ docker-build-full:
 .PHONY: docker-build-env
 
 docker-build-env-local:
+	docker-compose -f docker-compose.local.yml build
 	docker-compose -f docker-compose.local.yml up --remove-orphans
 .PHONY: docker-build-env-local
 
diff --git a/docker-compose.full.yml b/docker-compose.full.yml
index 107cb9e79064820145a26ad93cb636d8c32ea183..56a2996bf55d235e8e640d79e9d3677eedab2215 100644
--- a/docker-compose.full.yml
+++ b/docker-compose.full.yml
@@ -251,9 +251,25 @@ services:
       timeout: 5s
       retries: 5
 
+  etcd:
+    image: bitnami/etcd:3.5.1
+    volumes:
+      - etcd-data:/bitnami/etcd/data:rw
+    entrypoint: /opt/bitnami/etcd/bin/etcd
+    command:
+      - '--listen-client-urls=http://0.0.0.0:2379'
+      - '--advertise-client-urls=http://etcd:2379'
+      - '--data-dir=/bitnami/etcd/data'
+    networks:
+      - default
+    ports:
+      - 2379:2379
+      - 2380:2380
+
 volumes:
   pgsql-data:
     name: pgsql-data
-
+  etcd-data:
+    name: etcd-data
 networks:
   default:
diff --git a/docker-compose.local.yml b/docker-compose.local.yml
index 923b7b912f5a315956f66b3eaf22b81ce403b96e..35bf64c3eabc358dff6f541cb0567da8b2d7f093 100644
--- a/docker-compose.local.yml
+++ b/docker-compose.local.yml
@@ -41,6 +41,7 @@ services:
     extra_hosts:
       - "activemq:127.0.0.1"
       - "pg_db:127.0.0.1"
+      - "etcd:127.0.0.1"
 
 networks:
   shared:
diff --git a/docker-compose.test.yml b/docker-compose.test.yml
index 3fa813fb1778d9c8e45af93caf15d8147af4b9d3..bfe142d985cff7436f680774a5b404c2581fd681 100644
--- a/docker-compose.test.yml
+++ b/docker-compose.test.yml
@@ -78,9 +78,26 @@ services:
       timeout: 5s
       retries: 5
 
+  etcd:
+    image: bitnami/etcd:3.5.1
+    volumes:
+      - etcd-data:/bitnami/etcd/data:rw
+    entrypoint: /opt/bitnami/etcd/bin/etcd
+    command:
+      - '--listen-client-urls=http://0.0.0.0:2379'
+      - '--advertise-client-urls=http://etcd:2379'
+      - '--data-dir=/bitnami/etcd/data'
+    networks:
+      - default
+    ports:
+      - 2379:2379
+      - 2380:2380
+
 volumes:
   pgsql-data:
       name: pgsql-data
+  etcd-data:
+      name: etcd-data
 
 networks:
   default:
diff --git a/docker-compose.yml b/docker-compose.yml
index b0f24b3c2e5f2bf22b90eb97ce702d51307012d0..d82d2298f278a8d2bace94744df243d075f78db7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -82,9 +82,25 @@ services:
       timeout: 5s
       retries: 5
 
+  etcd:
+    image: bitnami/etcd:3.5.1
+    volumes:
+      - etcd-data:/bitnami/etcd/data:rw
+    entrypoint: /opt/bitnami/etcd/bin/etcd
+    command:
+      - '--listen-client-urls=http://0.0.0.0:2379'
+      - '--advertise-client-urls=http://etcd:2379'
+      - '--data-dir=/bitnami/etcd/data'
+    networks:
+      - default
+    ports:
+      - 2379:2379
+      - 2380:2380
+
 volumes:
   pgsql-data:
       name: pgsql-data
-
+  etcd-data:
+      name: etcd-data
 networks:
   default:
diff --git a/notifications_consumer/auditing.py b/notifications_consumer/auditing.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b39f96e5f18d1723b3428f46367c1a6af5c23e7
--- /dev/null
+++ b/notifications_consumer/auditing.py
@@ -0,0 +1,52 @@
+"""Auditing definition."""
+import json
+import logging
+import uuid
+from datetime import datetime
+
+import etcd3
+
+from notifications_consumer.config import Config
+
+
+client = etcd3.client(
+    host=Config.ETCD_HOST, port=Config.ETCD_PORT, user=Config.ETCD_USER, password=Config.ETCD_PASSWORD
+)
+
+
+def audit_notification(notification_id, value, key=None, user_id=None):
+    """Put audit notification information into audit DB."""
+    if Config.AUDITING is False:
+        logging.info("Audit disabled")
+        return
+
+    if not key:
+        key = uuid.uuid4()
+    try:
+        client.put(
+            (
+                f"/notifications/{notification_id}/{Config.AUDIT_ID}"
+                f"/{'target_users/' + user_id + '/' if user_id else ''}{key}"
+            ),
+            json.dumps({"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), **value}),
+        )
+    except Exception:
+        logging.exception("Error auditing to etcd3:")
+
+
+def get_audit_notification(notification_id, key, user_id=None):
+    """Get audit notification information from audit DB."""
+    if Config.AUDITING is False:
+        logging.info("Audit disabled")
+        return None
+
+    try:
+        return client.get(
+            (
+                f"/notifications/{notification_id}/{Config.AUDIT_ID}"
+                f"/{'target_users/' + user_id + '/' if user_id else ''}{key}"
+            )
+        )[0]
+    except Exception:
+        logging.exception("Error getting from etcd")
+        return None
diff --git a/notifications_consumer/config.py b/notifications_consumer/config.py
index b07196a34caaac18ef5222d7527773aff75182ad..4b442959f9274e5407074bc950874be2a4d46e08 100644
--- a/notifications_consumer/config.py
+++ b/notifications_consumer/config.py
@@ -134,6 +134,14 @@ class Config:
     # Cache
     CACHE_TTL = int(os.getenv("CACHE_TTL", 86400))
 
+    # Etcd auditing
+    ETCD_HOST = os.getenv("ETCD_HOST", "localhost")
+    ETCD_PORT = os.getenv("ETCD_PORT", 2379)
+    AUDITING = os.getenv("AUDITING", False)
+    ETCD_USER = os.getenv("ETCD_USER", None)
+    ETCD_PASSWORD = os.getenv("ETCD_PASSWORD", None)
+    AUDIT_ID = os.getenv("AUDIT_SERVICE_NAME", "consumer")
+
 
 class DevelopmentConfig(Config):
     """Development configuration overrides."""
diff --git a/notifications_consumer/processors/email/processor.py b/notifications_consumer/processors/email/processor.py
index 8827a1130115351874eeb0db41aefc7d715f932b..664d7b3754f3146b54b2ea853714b28c95ac7bed 100644
--- a/notifications_consumer/processors/email/processor.py
+++ b/notifications_consumer/processors/email/processor.py
@@ -3,6 +3,7 @@ import logging
 from datetime import datetime
 from typing import Dict
 
+from notifications_consumer.auditing import audit_notification, get_audit_notification
 from notifications_consumer.config import ENV_DEV, Config
 from notifications_consumer.email_whitelist import get_email_whitelist
 from notifications_consumer.processors.email.utils import create_email, send_emails
@@ -76,7 +77,15 @@ class EmailProcessor(Processor):
             context,
         )
 
-        return send_emails([email])
+        if get_audit_notification(kwargs["notification_id"], self.__id, recipient_email):
+            logging.warning(
+                "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], recipient_email
+            )
+            return
+
+        ret = send_emails([email])
+        audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, recipient_email)
+        return ret
 
     def read_message(self, message: Dict):
         """Read the message."""
diff --git a/notifications_consumer/processors/email_feed/processor.py b/notifications_consumer/processors/email_feed/processor.py
index a3bd0e46e48418664cfb0ba7dbe1938b626c0c40..47060f8e071fdafe51d3609e2e4b30f3560cd8bf 100644
--- a/notifications_consumer/processors/email_feed/processor.py
+++ b/notifications_consumer/processors/email_feed/processor.py
@@ -4,6 +4,7 @@ import logging
 from datetime import datetime
 from typing import Dict
 
+from notifications_consumer.auditing import audit_notification, get_audit_notification
 from notifications_consumer.config import ENV_DEV, Config
 from notifications_consumer.email_whitelist import get_email_whitelist
 from notifications_consumer.processors.email.utils import create_email, send_emails
@@ -74,7 +75,15 @@ class EmailFeedProcessor(Processor):
             context,
         )
 
-        return send_emails([email])
+        if get_audit_notification(kwargs["notification_id"], self.__id, recipient_email):
+            logging.warning(
+                "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], recipient_email
+            )
+            return
+
+        ret = send_emails([email])
+        audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, recipient_email)
+        return ret
 
     def read_message(self, message: Dict):
         """Read the message."""
diff --git a/notifications_consumer/processors/mattermost/processor.py b/notifications_consumer/processors/mattermost/processor.py
index 182e3aed697b7d4163f44f01a99ff945dcaf4357..ad6b9895e69fdeb5089d536120dd8554659d106b 100644
--- a/notifications_consumer/processors/mattermost/processor.py
+++ b/notifications_consumer/processors/mattermost/processor.py
@@ -2,9 +2,10 @@
 import logging
 from typing import Dict
 
+from notifications_consumer.auditing import audit_notification, get_audit_notification
+from notifications_consumer.processors.mattermost.utils import create_message, send_message
 from notifications_consumer.processors.processor import Processor
 from notifications_consumer.processors.registry import ProcessorRegistry
-from notifications_consumer.processors.mattermost.utils import create_message, send_message
 
 
 @ProcessorRegistry.register
@@ -36,7 +37,15 @@ class MattermostProcessor(Processor):
             kwargs["img_url"],
         )
 
-        return send_message(wpmessage, device_token)
+        if get_audit_notification(kwargs["notification_id"], self.__id, device_token):
+            logging.warning(
+                "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token
+            )
+            return
+
+        ret = send_message(wpmessage, device_token)
+        audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token)
+        return ret
 
     def read_message(self, message: Dict):
         """Read the message."""
diff --git a/notifications_consumer/processors/safaripush/processor.py b/notifications_consumer/processors/safaripush/processor.py
index 1b7031001a588eba395995d3b7dd64e64695bfb7..d0495f4056963ea8f2a25967986337e088810ed0 100644
--- a/notifications_consumer/processors/safaripush/processor.py
+++ b/notifications_consumer/processors/safaripush/processor.py
@@ -2,6 +2,7 @@
 import logging
 from typing import Dict
 
+from notifications_consumer.auditing import audit_notification, get_audit_notification
 from notifications_consumer.processors.processor import Processor
 from notifications_consumer.processors.registry import ProcessorRegistry
 from notifications_consumer.processors.safaripush.utils import create_message, send_message
@@ -36,7 +37,15 @@ class SafariPushProcessor(Processor):
             kwargs["img_url"],
         )
 
-        return send_message(spmessage, device_token)
+        if get_audit_notification(kwargs["notification_id"], self.__id, device_token):
+            logging.warning(
+                "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token
+            )
+            return
+
+        ret = send_message(spmessage, device_token)
+        audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token)
+        return ret
 
     def read_message(self, message: Dict):
         """Read the message."""
diff --git a/notifications_consumer/processors/webpush/processor.py b/notifications_consumer/processors/webpush/processor.py
index 94fef94f9e2470b311134f22037293364485f45c..506563770a4f395fe61f78dc27e779799a10ab1b 100644
--- a/notifications_consumer/processors/webpush/processor.py
+++ b/notifications_consumer/processors/webpush/processor.py
@@ -2,6 +2,7 @@
 import logging
 from typing import Dict
 
+from notifications_consumer.auditing import audit_notification, get_audit_notification
 from notifications_consumer.processors.processor import Processor
 from notifications_consumer.processors.registry import ProcessorRegistry
 from notifications_consumer.processors.webpush.utils import create_message, send_message
@@ -37,7 +38,15 @@ class WebPushProcessor(Processor):
             kwargs["img_url"],
         )
 
-        return send_message(wpmessage, device_token, encoding)
+        if get_audit_notification(kwargs["notification_id"], self.__id, device_token):
+            logging.warning(
+                "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token
+            )
+            return
+
+        ret = send_message(wpmessage, device_token, encoding)
+        audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token)
+        return ret
 
     def read_message(self, message: Dict):
         """Read the message."""
diff --git a/poetry.lock b/poetry.lock
index 64031fe06d412d353d2a3c251e5cd2fe0224a3cd..b15c9b14d2312d4527cd138f56f163fb8e8158ac 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -162,6 +162,20 @@ category = "main"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "etcd3"
+version = "0.12.0"
+description = "Python client for the etcd3 API"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+grpcio = ">=1.27.1"
+protobuf = ">=3.6.1"
+six = ">=1.12.0"
+tenacity = ">=6.1.0"
+
 [[package]]
 name = "filelock"
 version = "3.0.12"
@@ -204,6 +218,20 @@ category = "dev"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "grpcio"
+version = "1.43.0"
+description = "HTTP/2-based RPC framework"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+six = ">=1.5.2"
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.43.0)"]
+
 [[package]]
 name = "h2"
 version = "2.6.2"
@@ -515,6 +543,14 @@ python-versions = ">=3.6.1"
 [package.dependencies]
 wcwidth = "*"
 
+[[package]]
+name = "protobuf"
+version = "3.19.1"
+description = "Protocol Buffers"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
 [[package]]
 name = "psycopg2-binary"
 version = "2.8.6"
@@ -775,6 +811,17 @@ python-versions = ">=3.6,<4.0"
 [package.dependencies]
 docopt = ">=0.6.2,<0.7.0"
 
+[[package]]
+name = "tenacity"
+version = "8.0.1"
+description = "Retry code until it succeeds"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+doc = ["reno", "sphinx", "tornado (>=4.5)"]
+
 [[package]]
 name = "toml"
 version = "0.10.2"
@@ -879,7 +926,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.6.1"
-content-hash = "27d4bfdf8b68e1142fe5433e499d2b540e4b484b68ed7e0ee20fb92c7464b3d3"
+content-hash = "05231d3b1abbd4147ef58dca014d751df4ec651861a1d10c618b8dbbf551fb46"
 
 [metadata.files]
 apns2 = [
@@ -1008,6 +1055,9 @@ distlib = [
 docopt = [
     {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"},
 ]
+etcd3 = [
+    {file = "etcd3-0.12.0.tar.gz", hash = "sha256:89a704cb389bf0a010a1fa050ce19342d23bf6371ebda1c21cfe8ff3ed488726"},
+]
 filelock = [
     {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
@@ -1023,6 +1073,52 @@ flake8-docstrings = [
 flake8-logging-format = [
     {file = "flake8-logging-format-0.6.0.tar.gz", hash = "sha256:ca5f2b7fc31c3474a0aa77d227e022890f641a025f0ba664418797d979a779f8"},
 ]
+grpcio = [
+    {file = "grpcio-1.43.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:a4e786a8ee8b30b25d70ee52cda6d1dbba2a8ca2f1208d8e20ed8280774f15c8"},
+    {file = "grpcio-1.43.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:af9c3742f6c13575c0d4147a8454da0ff5308c4d9469462ff18402c6416942fe"},
+    {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:fdac966699707b5554b815acc272d81e619dd0999f187cd52a61aef075f870ee"},
+    {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e463b4aa0a6b31cf2e57c4abc1a1b53531a18a570baeed39d8d7b65deb16b7e"},
+    {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11d05402e0ac3a284443d8a432d3dfc76a6bd3f7b5858cddd75617af2d7bd9b"},
+    {file = "grpcio-1.43.0-cp310-cp310-win32.whl", hash = "sha256:c36f418c925a41fccada8f7ae9a3d3e227bfa837ddbfddd3d8b0ac252d12dda9"},
+    {file = "grpcio-1.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:772b943f34374744f70236bbbe0afe413ed80f9ae6303503f85e2b421d4bca92"},
+    {file = "grpcio-1.43.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:cbc9b83211d905859dcf234ad39d7193ff0f05bfc3269c364fb0d114ee71de59"},
+    {file = "grpcio-1.43.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:fb7229fa2a201a0c377ff3283174ec966da8f9fd7ffcc9a92f162d2e7fc9025b"},
+    {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:17b75f220ee6923338155b4fcef4c38802b9a57bc57d112c9599a13a03e99f8d"},
+    {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:6620a5b751b099b3b25553cfc03dfcd873cda06f9bb2ff7e9948ac7090e20f05"},
+    {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:1898f999383baac5fcdbdef8ea5b1ef204f38dc211014eb6977ac6e55944d738"},
+    {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47b6821238d8978014d23b1132713dac6c2d72cbb561cf257608b1673894f90a"},
+    {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80398e9fb598060fa41050d1220f5a2440fe74ff082c36dda41ac3215ebb5ddd"},
+    {file = "grpcio-1.43.0-cp36-cp36m-win32.whl", hash = "sha256:0110310eff07bb69782f53b7a947490268c4645de559034c43c0a635612e250f"},
+    {file = "grpcio-1.43.0-cp36-cp36m-win_amd64.whl", hash = "sha256:45401d00f2ee46bde75618bf33e9df960daa7980e6e0e7328047191918c98504"},
+    {file = "grpcio-1.43.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:af78ac55933811e6a25141336b1f2d5e0659c2f568d44d20539b273792563ca7"},
+    {file = "grpcio-1.43.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8b2b9dc4d7897566723b77422e11c009a0ebd397966b165b21b89a62891a9fdf"},
+    {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:77ef653f966934b3bfdd00e4f2064b68880eb40cf09b0b99edfa5ee22a44f559"},
+    {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e95b5d62ec26d0cd0b90c202d73e7cb927c369c3358e027225239a4e354967dc"},
+    {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:04239e8f71db832c26bbbedb4537b37550a39d77681d748ab4678e58dd6455d6"},
+    {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b4a7152187a49767a47d1413edde2304c96f41f7bc92cc512e230dfd0fba095"},
+    {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8cc936a29c65ab39714e1ba67a694c41218f98b6e2a64efb83f04d9abc4386b"},
+    {file = "grpcio-1.43.0-cp37-cp37m-win32.whl", hash = "sha256:577e024c8dd5f27cd98ba850bc4e890f07d4b5942e5bc059a3d88843a2f48f66"},
+    {file = "grpcio-1.43.0-cp37-cp37m-win_amd64.whl", hash = "sha256:138f57e3445d4a48d9a8a5af1538fdaafaa50a0a3c243f281d8df0edf221dc02"},
+    {file = "grpcio-1.43.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:08cf25f2936629db062aeddbb594bd76b3383ab0ede75ef0461a3b0bc3a2c150"},
+    {file = "grpcio-1.43.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:01f4b887ed703fe82ebe613e1d2dadea517891725e17e7a6134dcd00352bd28c"},
+    {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0aa8285f284338eb68962fe1a830291db06f366ea12f213399b520c062b01f65"},
+    {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0edbfeb6729aa9da33ce7e28fb7703b3754934115454ae45e8cc1db601756fd3"},
+    {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:c354017819201053d65212befd1dcb65c2d91b704d8977e696bae79c47cd2f82"},
+    {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50cfb7e1067ee5e00b8ab100a6b7ea322d37ec6672c0455106520b5891c4b5f5"},
+    {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f1aeb65ed17dfb2f6cd717cc109910fe395133af7257a9c729c0b9604eac10"},
+    {file = "grpcio-1.43.0-cp38-cp38-win32.whl", hash = "sha256:fa26a8bbb3fe57845acb1329ff700d5c7eaf06414c3e15f4cb8923f3a466ef64"},
+    {file = "grpcio-1.43.0-cp38-cp38-win_amd64.whl", hash = "sha256:ade8b79a6b6aea68adb9d4bfeba5d647667d842202c5d8f3ba37ac1dc8e5c09c"},
+    {file = "grpcio-1.43.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:124e718faf96fe44c98b05f3f475076be8b5198bb4c52a13208acf88a8548ba9"},
+    {file = "grpcio-1.43.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2f96142d0abc91290a63ba203f01649e498302b1b6007c67bad17f823ecde0cf"},
+    {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:31e6e489ccd8f08884b9349a39610982df48535881ec34f05a11c6e6b6ebf9d0"},
+    {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0e731f660e1e68238f56f4ce11156f02fd06dc58bc7834778d42c0081d4ef5ad"},
+    {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:1f16725a320460435a8a5339d8b06c4e00d307ab5ad56746af2e22b5f9c50932"},
+    {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b4543e13acb4806917d883d0f70f21ba93b29672ea81f4aaba14821aaf9bb0"},
+    {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:594aaa0469f4fca7773e80d8c27bf1298e7bbce5f6da0f084b07489a708f16ab"},
+    {file = "grpcio-1.43.0-cp39-cp39-win32.whl", hash = "sha256:5449ae564349e7a738b8c38583c0aad954b0d5d1dd3cea68953bfc32eaee11e3"},
+    {file = "grpcio-1.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:bdf41550815a831384d21a498b20597417fd31bd084deb17d31ceb39ad9acc79"},
+    {file = "grpcio-1.43.0.tar.gz", hash = "sha256:735d9a437c262ab039d02defddcb9f8f545d7009ae61c0114e19dda3843febe5"},
+]
 h2 = [
     {file = "h2-2.6.2-py2.py3-none-any.whl", hash = "sha256:93cbd1013a2218539af05cdf9fc37b786655b93bbc94f5296b7dabd1c5cadf41"},
     {file = "h2-2.6.2.tar.gz", hash = "sha256:af35878673c83a44afbc12b13ac91a489da2819b5dc1e11768f3c2406f740fe9"},
@@ -1157,6 +1253,32 @@ prompt-toolkit = [
     {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"},
     {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"},
 ]
+protobuf = [
+    {file = "protobuf-3.19.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8"},
+    {file = "protobuf-3.19.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d"},
+    {file = "protobuf-3.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f"},
+    {file = "protobuf-3.19.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6"},
+    {file = "protobuf-3.19.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6"},
+    {file = "protobuf-3.19.1-cp36-cp36m-win32.whl", hash = "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c"},
+    {file = "protobuf-3.19.1-cp36-cp36m-win_amd64.whl", hash = "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942"},
+    {file = "protobuf-3.19.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853"},
+    {file = "protobuf-3.19.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d"},
+    {file = "protobuf-3.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6"},
+    {file = "protobuf-3.19.1-cp37-cp37m-win32.whl", hash = "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04"},
+    {file = "protobuf-3.19.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea"},
+    {file = "protobuf-3.19.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7"},
+    {file = "protobuf-3.19.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089"},
+    {file = "protobuf-3.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e"},
+    {file = "protobuf-3.19.1-cp38-cp38-win32.whl", hash = "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3"},
+    {file = "protobuf-3.19.1-cp38-cp38-win_amd64.whl", hash = "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b"},
+    {file = "protobuf-3.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d"},
+    {file = "protobuf-3.19.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995"},
+    {file = "protobuf-3.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560"},
+    {file = "protobuf-3.19.1-cp39-cp39-win32.whl", hash = "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2"},
+    {file = "protobuf-3.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002"},
+    {file = "protobuf-3.19.1-py2.py3-none-any.whl", hash = "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17"},
+    {file = "protobuf-3.19.1.tar.gz", hash = "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7"},
+]
 psycopg2-binary = [
     {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"},
     {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"},
@@ -1338,6 +1460,10 @@ sqlalchemy = [
     {file = "stomp.py-6.1.0-py3-none-any.whl", hash = "sha256:8a1ed68cd8b12f41ba56a8dfeff995e7866d1d47ed7f53aaa78da3bea44696b8"},
     {file = "stomp.py-6.1.0.tar.gz", hash = "sha256:1f6c7e1e5089b1d8a75161e66533cabb9895de5121cc3900cb7e12c41c1bda18"},
 ]
+tenacity = [
+    {file = "tenacity-8.0.1-py3-none-any.whl", hash = "sha256:f78f4ea81b0fabc06728c11dc2a8c01277bfc5181b321a4770471902e3eb844a"},
+    {file = "tenacity-8.0.1.tar.gz", hash = "sha256:43242a20e3e73291a28bcbcacfd6e000b02d3857a9a9fff56b297a27afdc932f"},
+]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
diff --git a/pyproject.toml b/pyproject.toml
index 889db5cd2b362a0b88cd45983ec0deacfd17c55f..6fd7211c6df986e74e0326d62f0921498c231af0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,6 +22,7 @@ pycrypto = "^2.6.1"
 python-smail = "^0.9.0"
 mattermostdriver = "^7.3.1"
 cachetools = "^4.2.4"
+etcd3 = "^0.12.0"
 
 [tool.poetry.dev-dependencies]
 pre-commit = "~2.9.2"