diff --git a/.env b/.env
index 350135560d3909692ca122094f3577b1418f8128..14e070594156cefa334ce3410afd074b34000c0c 100644
--- a/.env
+++ b/.env
@@ -24,15 +24,13 @@ EMAIL_HOST_PASSWORD=password
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
 
 CERN_OIDC_CLIENT_ID=notifications-dev
 CERN_OIDC_CLIENT_SECRET=fill-me
 
-EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
-
 # SMIME signed emails
 #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
-
diff --git a/.env-email-daily b/.env-email-daily
index da378cbd41dc857e0b113eb339441e05586d5723..dbea626cfeedad5aeb3dd86c9f6433248ba1eb4f 100644
--- a/.env-email-daily
+++ b/.env-email-daily
@@ -24,6 +24,10 @@ EMAIL_HOST_PASSWORD=password
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
+
+CERN_OIDC_CLIENT_ID=notifications-dev
+CERN_OIDC_CLIENT_SECRET=fill-me
 
 FEED_TITLE=Daily
diff --git a/.env-email-monthly b/.env-email-monthly
index 9648d9aa9cb536f6b71e29d99d24fed004c5a232..7bb1ce4ff696492a08a3376316d3345228f8a009 100644
--- a/.env-email-monthly
+++ b/.env-email-monthly
@@ -24,6 +24,10 @@ EMAIL_HOST_PASSWORD=password
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
+
+CERN_OIDC_CLIENT_ID=notifications-dev
+CERN_OIDC_CLIENT_SECRET=fill-me
 
 FEED_TITLE=Monthly
diff --git a/.env-email-weekly b/.env-email-weekly
index d26d3b454bf4ba3ddf102128ffd3c6c1f0480770..5b346345ae40329cdaf3e454bd30418e27ba80d8 100644
--- a/.env-email-weekly
+++ b/.env-email-weekly
@@ -2,7 +2,6 @@ ENV=development
 PROCESSOR=email_feed
 CONSUMER_NAME=email_weekly_consumer
 PUBLISHER_NAME=email_weekly_publisher
-FEED_SUMMARY=Weekly
 
 POSTGRES_USER=admin
 POSTGRES_PASSWORD=password
@@ -25,6 +24,10 @@ EMAIL_HOST_PASSWORD=password
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
+
+CERN_OIDC_CLIENT_ID=notifications-dev
+CERN_OIDC_CLIENT_SECRET=fill-me
 
 FEED_TITLE=Weekly
diff --git a/.env.email-dlq b/.env.email-dlq
index c0dcaf097d5c4718629cb5ecc8414b3d5e6db857..5f98575624b4ce806c01e81b92c9592b1e2fb529 100644
--- a/.env.email-dlq
+++ b/.env.email-dlq
@@ -24,4 +24,8 @@ EMAIL_HOST_PASSWORD=password
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
+
+CERN_OIDC_CLIENT_ID=notifications-dev
+CERN_OIDC_CLIENT_SECRET=fill-me
diff --git a/.env.email-gateway-failure b/.env.email-gateway-failure
index d620cf7e9a657e4a8786fc6dc2bd59c390c111b7..a5bc79512aa825ee91f0d34b49e04f9b42e60069 100644
--- a/.env.email-gateway-failure
+++ b/.env.email-gateway-failure
@@ -23,4 +23,8 @@ EMAIL_HOST_USER=user@cern.ch
 EMAIL_USE_TLS=None
 EMAIL_USE_SSL=None
 EMAIL_TIMEOUT=10
-EMAIL_WHITELIST=["user@cern.ch"]
+EMAIL_WHITELIST_GROUP_ID=notifications-dev-users
+EMAIL_AES_SECRET_KEY=fill-mefill-mefill-mefill-mefill
+
+CERN_OIDC_CLIENT_ID=notifications-dev
+CERN_OIDC_CLIENT_SECRET=fill-me
diff --git a/.isort.cfg b/.isort.cfg
index 3213b0f88e550f3e8a5e76d58b3891e1966ed14c..918f3dd28c505839262a666e62be15db69e29191 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,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml
+known_third_party = Crypto,apns2,cachetools,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml
diff --git a/README.md b/README.md
index 8ef9c761c2496ae1d8f6cf84931b6d60d88699b6..6e7203b20bdb2655ac7fd68140973e2a697109c9 100644
--- a/README.md
+++ b/README.md
@@ -171,7 +171,6 @@ Requirements:
     EMAIL_USE_TLS=None
     EMAIL_USE_SSL=None
     EMAIL_TIMEOUT=10
-    EMAIL_WHITELIST=["<your-email>@cern.ch"]
     ```
     Note: `EMAIL_HOST_PASSWORD` can't be in the `.env`.
 
diff --git a/notifications_consumer/authorization_service.py b/notifications_consumer/authorization_service.py
index 047988042e79a64eb0884261c0a4301aa236bf6a..735a45d382c613dcd856ea10430f85e6f02a48a3 100644
--- a/notifications_consumer/authorization_service.py
+++ b/notifications_consumer/authorization_service.py
@@ -33,3 +33,17 @@ def get_group_users_api(group_id: str):
         raise BadResponseCodeError(url, status_code=response.status_code)
 
     return json.loads(response.content)
+
+
+def get_group_member_groups_api(group_id: str):
+    """Get member groups of a group."""
+    token = get_auth_access_token()
+    headers = {"Authorization": "Bearer {}".format(token)}
+
+    url = os.path.join(config.Config.CERN_GROUP_URL, group_id, config.Config.CERN_GROUP_MEMBER_GROUPS_QUERY)
+    response = requests.get(url, headers=headers)
+
+    if response.status_code != requests.codes.ok:
+        raise BadResponseCodeError(url, status_code=response.status_code)
+
+    return json.loads(response.content)
diff --git a/notifications_consumer/config.py b/notifications_consumer/config.py
index a1d3dd4d4245002654e8d96a18e4beaa5a7c0df5..b07196a34caaac18ef5222d7527773aff75182ad 100644
--- a/notifications_consumer/config.py
+++ b/notifications_consumer/config.py
@@ -38,6 +38,9 @@ class Config:
     CERN_GROUP_QUERY = os.getenv(
         "CERN_GROUP_QUERY", "memberidentities?field=upn&field=primaryAccountEmail&recursive=true"
     )
+    CERN_GROUP_MEMBER_GROUPS_QUERY = os.getenv(
+        "CERN_GROUP_MEMBER_GROUPS_QUERY", "membergroups?field=idl&recursive=true"
+    )
 
     # ActiveMQ
     CONSUMER_NAME = os.getenv("CONSUMER_NAME")
@@ -52,7 +55,7 @@ class Config:
     EMAIL_USE_TLS = ast.literal_eval(os.getenv("EMAIL_USE_TLS", "False"))
     EMAIL_USE_SSL = ast.literal_eval(os.getenv("EMAIL_USE_SSL", "False"))
     EMAIL_TIMEOUT = int(os.getenv("EMAIL_TIMEOUT", "10"))
-    EMAIL_WHITELIST = ast.literal_eval(os.getenv("EMAIL_WHITELIST", "['user@cern.ch']"))
+    EMAIL_WHITELIST_GROUP_ID = os.getenv("EMAIL_WHITELIST_GROUP_ID", "notifications-dev-users")
 
     EMAIL_BACKEND = os.getenv("EMAIL_BACKEND", "vendor.django_mail.backends.console.EmailBackend")
 
@@ -128,6 +131,9 @@ class Config:
     # Feed email title
     FEED_TITLE = os.getenv("FEED_TITLE", "Daily")
 
+    # Cache
+    CACHE_TTL = int(os.getenv("CACHE_TTL", 86400))
+
 
 class DevelopmentConfig(Config):
     """Development configuration overrides."""
diff --git a/notifications_consumer/email_whitelist.py b/notifications_consumer/email_whitelist.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bb7085c72c80376d45a8ad6976c684bba400e78
--- /dev/null
+++ b/notifications_consumer/email_whitelist.py
@@ -0,0 +1,30 @@
+"""Email whitelist methods."""
+
+from typing import Set
+
+from cachetools import TTLCache, cached
+
+from notifications_consumer.authorization_service import get_group_member_groups_api, get_group_users_api
+from notifications_consumer.config import Config
+
+
+@cached(cache=TTLCache(maxsize=1024, ttl=Config.CACHE_TTL))
+def get_email_whitelist(group_id: str) -> Set[str]:
+    """Return all the members' emails of the whitelist group."""
+    email_whitelist = get_group_members_emails(group_id)
+
+    group_member_groups_response = get_group_member_groups_api(group_id)
+
+    if group_member_groups_response["data"]:
+        for member_group in group_member_groups_response["data"]:
+            emails = get_group_members_emails(member_group["id"])
+        email_whitelist.update(emails)
+
+    return email_whitelist
+
+
+def get_group_members_emails(group_id: str) -> Set[str]:
+    """Return the emails of group members."""
+    group_members_response = get_group_users_api(group_id)
+
+    return {member["primaryAccountEmail"] for member in group_members_response["data"]}
diff --git a/notifications_consumer/processors/email/processor.py b/notifications_consumer/processors/email/processor.py
index 3bac1fca09d930e96cdf24c3aacff59572350cb5..8827a1130115351874eeb0db41aefc7d715f932b 100644
--- a/notifications_consumer/processors/email/processor.py
+++ b/notifications_consumer/processors/email/processor.py
@@ -4,6 +4,7 @@ from datetime import datetime
 from typing import Dict
 
 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
 from notifications_consumer.processors.processor import Processor
 from notifications_consumer.processors.registry import ProcessorRegistry
@@ -30,9 +31,15 @@ class EmailProcessor(Processor):
         logging.debug("%s - status:running - kwargs:%r", self, kwargs)
 
         recipient_email = kwargs["email"]
-        if Config.ENV == ENV_DEV and recipient_email not in Config.EMAIL_WHITELIST:
-            logging.info("Email recipient <%s> isn't on the EMAIL_WHITELIST. Email sent is skipped.", recipient_email)
-            return
+        if Config.ENV == ENV_DEV:
+            email_whitelist = get_email_whitelist(Config.EMAIL_WHITELIST_GROUP_ID)
+            if recipient_email not in email_whitelist:
+                logging.info(
+                    "Email recipient <%s> isn't on the email whitelist (<%s>). Email sent is skipped.",
+                    recipient_email,
+                    Config.EMAIL_WHITELIST_GROUP_ID,
+                )
+                return
 
         created_at = kwargs.get("created_at", "")
         try:
diff --git a/notifications_consumer/processors/email_feed/processor.py b/notifications_consumer/processors/email_feed/processor.py
index 9f3eadd5f6eaabfbe41603bc1b6e0d7dcd1e4ab6..a3bd0e46e48418664cfb0ba7dbe1938b626c0c40 100644
--- a/notifications_consumer/processors/email_feed/processor.py
+++ b/notifications_consumer/processors/email_feed/processor.py
@@ -5,6 +5,7 @@ from datetime import datetime
 from typing import Dict
 
 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
 from notifications_consumer.processors.processor import Processor
 from notifications_consumer.processors.registry import ProcessorRegistry
@@ -37,9 +38,15 @@ class EmailFeedProcessor(Processor):
 
         user_id = kwargs["user_id"]
         recipient_email = self.data_source.get_user_email(user_id)
-        if Config.ENV == ENV_DEV and recipient_email not in Config.EMAIL_WHITELIST:
-            logging.info("Email recipient <%s> isn't on the EMAIL_WHITELIST. Email sent is skipped.", recipient_email)
-            return
+        if Config.ENV == ENV_DEV:
+            email_whitelist = get_email_whitelist(Config.EMAIL_WHITELIST_GROUP_ID)
+            if recipient_email not in email_whitelist:
+                logging.info(
+                    "Email recipient <%s> isn't on the email whitelist (<%s>). Email sent is skipped.",
+                    recipient_email,
+                    Config.EMAIL_WHITELIST_GROUP_ID,
+                )
+                return
 
         title = f'{Config.FEED_TITLE} Summary - {datetime.now().strftime("%d %B %Y")}'
         subject = f"[{Config.SERVICE_NAME}] {title}"
diff --git a/poetry.lock b/poetry.lock
index ef7a89a562765c5160644cc6b06defb22995fe48..64031fe06d412d353d2a3c251e5cd2fe0224a3cd 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -22,6 +22,14 @@ category = "dev"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "appnope"
+version = "0.1.2"
+description = "Disable App Nap on macOS >= 10.9"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "asn1crypto"
 version = "1.4.0"
@@ -52,6 +60,22 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
 tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
 tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
 
+[[package]]
+name = "backcall"
+version = "0.2.0"
+description = "Specifications for callback functions passed in to an API"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cachetools"
+version = "4.2.4"
+description = "Extensible memoizing collections and decorators"
+category = "main"
+optional = false
+python-versions = "~=3.5"
+
 [[package]]
 name = "certifi"
 version = "2020.12.5"
@@ -114,6 +138,14 @@ sdist = ["setuptools-rust (>=0.11.4)"]
 ssh = ["bcrypt (>=3.1.5)"]
 test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
 
+[[package]]
+name = "decorator"
+version = "5.1.0"
+description = "Decorators for Humans"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
 [[package]]
 name = "distlib"
 version = "0.3.1"
@@ -284,6 +316,60 @@ category = "main"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "ipython"
+version = "7.16.2"
+description = "IPython: Productive Interactive Computing"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+appnope = {version = "*", markers = "sys_platform == \"darwin\""}
+backcall = "*"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+decorator = "*"
+jedi = ">=0.10,<=0.17.2"
+pexpect = {version = "*", markers = "sys_platform != \"win32\""}
+pickleshare = "*"
+prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
+pygments = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"]
+doc = ["Sphinx (>=1.3)"]
+kernel = ["ipykernel"]
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["notebook", "ipywidgets"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"]
+
+[[package]]
+name = "ipython-genutils"
+version = "0.2.0"
+description = "Vestigial utilities from IPython"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "jedi"
+version = "0.17.2"
+description = "An autocompletion tool for Python that can be used for text editors."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+parso = ">=0.7.0,<0.8.0"
+
+[package.extras]
+qa = ["flake8 (==3.7.9)"]
+testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"]
+
 [[package]]
 name = "jinja2"
 version = "2.11.3"
@@ -356,6 +442,36 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 [package.dependencies]
 pyparsing = ">=2.0.2"
 
+[[package]]
+name = "parso"
+version = "0.7.1"
+description = "A Python Parser"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+testing = ["docopt", "pytest (>=3.0.7)"]
+
+[[package]]
+name = "pexpect"
+version = "4.8.0"
+description = "Pexpect allows easy control of interactive console applications."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ptyprocess = ">=0.5"
+
+[[package]]
+name = "pickleshare"
+version = "0.7.5"
+description = "Tiny 'shelve'-like database with concurrency support"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "pluggy"
 version = "0.13.1"
@@ -388,6 +504,17 @@ pyyaml = ">=5.1"
 toml = "*"
 virtualenv = ">=20.0.8"
 
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.19"
+description = "Library for building powerful interactive command lines in Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+wcwidth = "*"
+
 [[package]]
 name = "psycopg2-binary"
 version = "2.8.6"
@@ -396,6 +523,14 @@ category = "main"
 optional = false
 python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
 
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+description = "Run a subprocess in a pseudo terminal"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "py"
 version = "1.10.0"
@@ -458,6 +593,14 @@ category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
+[[package]]
+name = "pygments"
+version = "2.10.0"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
 [[package]]
 name = "pyjwt"
 version = "1.7.1"
@@ -640,6 +783,22 @@ category = "main"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 
+[[package]]
+name = "traitlets"
+version = "4.3.3"
+description = "Traitlets Python config system"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+decorator = "*"
+ipython-genutils = "*"
+six = "*"
+
+[package.extras]
+test = ["pytest", "mock"]
+
 [[package]]
 name = "typing-extensions"
 version = "3.10.0.0"
@@ -689,6 +848,14 @@ six = ">=1.9.0,<2"
 docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
 testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
 
+[[package]]
+name = "wcwidth"
+version = "0.2.5"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "websockets"
 version = "9.1"
@@ -712,7 +879,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 = "7373c8e971edeb715dccf372829746539216d2754488c51284df267389a8aa75"
+content-hash = "27d4bfdf8b68e1142fe5433e499d2b540e4b484b68ed7e0ee20fb92c7464b3d3"
 
 [metadata.files]
 apns2 = [
@@ -723,6 +890,10 @@ appdirs = [
     {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
     {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
 ]
+appnope = [
+    {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
+    {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
+]
 asn1crypto = [
     {file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"},
     {file = "asn1crypto-1.4.0.tar.gz", hash = "sha256:f4f6e119474e58e04a2b1af817eb585b4fd72bdd89b998624712b5c99be7641c"},
@@ -735,6 +906,14 @@ attrs = [
     {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
     {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
 ]
+backcall = [
+    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
+    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
+]
+cachetools = [
+    {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"},
+    {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"},
+]
 certifi = [
     {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
     {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
@@ -818,6 +997,10 @@ cryptography = [
     {file = "cryptography-3.4.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3"},
     {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"},
 ]
+decorator = [
+    {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"},
+    {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"},
+]
 distlib = [
     {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
     {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
@@ -879,6 +1062,18 @@ iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
+ipython = [
+    {file = "ipython-7.16.2-py3-none-any.whl", hash = "sha256:2f644313be4fdc5c8c2a17467f2949c29423c9e283a159d1fc9bf450a1a300af"},
+    {file = "ipython-7.16.2.tar.gz", hash = "sha256:613085f8acb0f35f759e32bea35fba62c651a4a2e409a0da11414618f5eec0c4"},
+]
+ipython-genutils = [
+    {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
+    {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
+]
+jedi = [
+    {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"},
+    {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"},
+]
 jinja2 = [
     {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
     {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
@@ -938,6 +1133,18 @@ packaging = [
     {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
     {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
 ]
+parso = [
+    {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"},
+    {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"},
+]
+pexpect = [
+    {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
+    {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
+]
+pickleshare = [
+    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
+    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+]
 pluggy = [
     {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
@@ -946,6 +1153,10 @@ pre-commit = [
     {file = "pre_commit-2.9.3-py2.py3-none-any.whl", hash = "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0"},
     {file = "pre_commit-2.9.3.tar.gz", hash = "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"},
 ]
+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"},
+]
 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"},
@@ -983,6 +1194,10 @@ psycopg2-binary = [
     {file = "psycopg2_binary-2.8.6-cp39-cp39-win32.whl", hash = "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056"},
     {file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"},
 ]
+ptyprocess = [
+    {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
+    {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
+]
 py = [
     {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
     {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
@@ -1009,6 +1224,10 @@ pyflakes = [
     {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
     {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
 ]
+pygments = [
+    {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
+    {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
+]
 pyjwt = [
     {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"},
     {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"},
@@ -1123,6 +1342,10 @@ toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
 ]
+traitlets = [
+    {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
+    {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
+]
 typing-extensions = [
     {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
     {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
@@ -1139,6 +1362,10 @@ virtualenv = [
     {file = "virtualenv-20.4.6-py2.py3-none-any.whl", hash = "sha256:307a555cf21e1550885c82120eccaf5acedf42978fd362d32ba8410f9593f543"},
     {file = "virtualenv-20.4.6.tar.gz", hash = "sha256:72cf267afc04bf9c86ec932329b7e94db6a0331ae9847576daaa7ca3c86b29a4"},
 ]
+wcwidth = [
+    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
 websockets = [
     {file = "websockets-9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d144b350045c53c8ff09aa1cfa955012dd32f00c7e0862c199edcabb1a8b32da"},
     {file = "websockets-9.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b4ad84b156cf50529b8ac5cc1638c2cf8680490e3fccb6121316c8c02620a2e4"},
diff --git a/pyproject.toml b/pyproject.toml
index aed62f7541e85155d256fdd76bed2b51261f83fa..889db5cd2b362a0b88cd45983ec0deacfd17c55f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,12 +21,14 @@ unittest = "^0.0"
 pycrypto = "^2.6.1"
 python-smail = "^0.9.0"
 mattermostdriver = "^7.3.1"
+cachetools = "^4.2.4"
 
 [tool.poetry.dev-dependencies]
 pre-commit = "~2.9.2"
 flake8 = "~3.8.3"
 flake8-docstrings = "~1.5.0"
 flake8-logging-format = "^0.6.0"
+ipython = ">=7.14"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]
diff --git a/scripts/docker-send-email.py b/scripts/docker-send-email.py
index 4a8c054ff57bdde2c1690d1d985822be67b7d392..7321e0b1ccae64ab5ab90bf32639f325c2439d2e 100644
--- a/scripts/docker-send-email.py
+++ b/scripts/docker-send-email.py
@@ -1,12 +1,14 @@
 """Utility to send one message inside docker environment."""
-import stomp
 import argparse
 import json
 
-parser = argparse.ArgumentParser(description='Send a test email notification.')
-parser.add_argument('-l', action='store_true', default=False, help='use to connect to localhost instead of activemq')
-parser.add_argument('-c', action='store_true', default=False, help='use to send an email with Category')
-parser.add_argument('-e', default='user', help='use to set a target email username')
+import stomp
+
+
+parser = argparse.ArgumentParser(description="Send a test email notification.")
+parser.add_argument("-l", action="store_true", default=False, help="use to connect to localhost instead of activemq")
+parser.add_argument("-c", action="store_true", default=False, help="use to send an email with Category")
+parser.add_argument("-e", default="user@cern.ch", help="use to set a target email username")
 args = parser.parse_args()
 category = args.c
 email = args.e
@@ -18,18 +20,19 @@ conn = stomp.Connection([(connection_target, 61613)])
 conn.connect("admin", "admin", wait=True)
 
 message = {
-"channel_name": "My Channel",
-"channel_id": "123",
-"message_body": "<p>Dear subscribers,</p>\n\n<p>This week news follows.&nbsp;</p>\n\n<p>&nbsp;</p>\n\n<p><strong>Topic 1</strong></p>\n\n<ul>\n\t<li>bla&nbsp;</li>\n\t<li>bla</li>\n\t<li>bla</li>\n</ul>\n\n<p>&nbsp;</p>\n\n<p><strong>Topic 2</strong></p>\n\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n\n<p>&nbsp;</p>\n\n<p>See you next week,</p>\n\n<p>Myself.</p>\n",
-"summary": "This week news!",
-"created_at": "07/21/2021, 16:10:09",
-"notification_id": "BD19EEA4-9DCA-48D9-A577-5DE9D2BF374A",
-"link": "http://toto.cern.ch",
-"priority": "important"
+    "channel_name": "My Channel",
+    "channel_id": "123",
+    "message_body": "<p>Dear subscribers,</p>\n\n<p>This week news follows.&nbsp;</p>\n\n<p>&nbsp;</p>\n\n<p><strong>Topic 1</strong></p>\n\n<ul>\n\t<li>bla&nbsp;</li>\n\t<li>bla</li>\n\t<li>bla</li>\n</ul>\n\n<p>&nbsp;</p>\n\n<p><strong>Topic 2</strong></p>\n\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n\n<p>&nbsp;</p>\n\n<p>See you next week,</p>\n\n<p>Myself.</p>\n",
+    "summary": "This week news!",
+    "created_at": "07/21/2021, 16:10:09",
+    "notification_id": "BD19EEA4-9DCA-48D9-A577-5DE9D2BF374A",
+    "link": "http://toto.cern.ch",
+    "priority": "important",
 }
 
-message["email"] = email + "@cern.ch"
-if category: message["category_name"] = "Super Duper Category" 
+message["email"] = email
+if category:
+    message["category_name"] = "Super Duper Category"
 
-conn.send(body=json.dumps(message, indent = 4), destination="/queue/np.email", headers={"persistent": "true"})
+conn.send(body=json.dumps(message, indent=4), destination="/queue/np.email", headers={"persistent": "true"})
 conn.disconnect()