diff --git a/api/config/live_config.json b/api/config/live_config.json
index 8b335dcc36b8b2e83da3c5a631485b431345e521..e1b0508c54a50985cdace332c16f38a37b73d1fa 100644
--- a/api/config/live_config.json
+++ b/api/config/live_config.json
@@ -3,10 +3,11 @@
   "disabled": false,
   "static_announcements": [
     {
-      "is_enabled": false,
+      "is_enabled": true,
+      "visible": true,
       "type": "info",
-      "visibility": "all_pages",
-      "text": "Example static announcement. Other types: 'warning' and 'important'. Other visibilities: 'only_main_page' and 'all_pages_and_embed'"
+      "page_visibility": "all_pages",
+      "text": "Example static announcement. 'visible' refers to publicly visible or only for mods. Other types: 'warning' and 'important'. Other visibilities: 'only_main_page' and 'all_pages_and_embed'"
     }
   ]
 }
\ No newline at end of file
diff --git a/api/src/api/live_config.py b/api/src/api/live_config.py
index b7fab120e23faed6b2acdf630fdc504c7515b6a4..19a7a7ba700d772548f85b73b474eba43379a161 100644
--- a/api/src/api/live_config.py
+++ b/api/src/api/live_config.py
@@ -2,6 +2,8 @@ import os
 import traceback
 from pathlib import Path
 
+from videoag_common.objects import AnnouncementType, AnnouncementPageVisibility
+
 import api
 from api.miscellaneous import *
 
@@ -9,63 +11,58 @@ _LIVE_CONFIG_PATH_STRING = os.environ.get("VIDEOAG_API_LIVE_CONFIG", None)
 _LIVE_CONFIG_PATH = None if _LIVE_CONFIG_PATH_STRING is None else Path(_LIVE_CONFIG_PATH_STRING).resolve()
 
 
+class LiveConfigAnnouncement(JsonDataClass):
+    is_enabled: bool
+    visible: bool
+    type: AnnouncementType
+    page_visibility: AnnouncementPageVisibility
+    text: str = json_field(max_length=8192)
+
+
+class LiveConfigData(JsonDataClass):
+    readonly: bool
+    disabled: bool
+    static_announcements: list[LiveConfigAnnouncement]
+
+
 class LiveConfig:
     
     def __init__(self):
         super().__init__()
-        self._disabled: bool = False
-        self._readonly: bool = False
-        self._static_announcements: list[dict] = []
+        self.reset()
     
     def reset(self):
-        self._disabled = False
-        self._readonly = False
-        self._static_announcements = []
+        self._data = LiveConfigData(
+            readonly=False,
+            disabled=False,
+            static_announcements=[],
+        )
     
     def update(self, json_config: CJsonObject):
         """
         Raises an exception if the file has wrong format/values. In this case, nothing is updated
         """
-        disable = json_config.get_bool("disabled", False)
-        readonly = json_config.get_bool("readonly", False) or disable
-        static_announcements = []
-        unsafe_announcements = json_config.get_array("static_announcements")
-        if unsafe_announcements is not None:
-            for unsafe_announcement in unsafe_announcements:
-                unsafe_announcement = unsafe_announcement.as_object()
-                
-                type = unsafe_announcement.get_string("type", 100)
-                if type not in ["info", "warning", "important"]:
-                    unsafe_announcement.get("type").raise_error("Unknown type")
-                visibility = unsafe_announcement.get_string("visibility", 100)
-                if visibility not in ["only_main_page", "all_pages", "all_pages_and_embed"]:
-                    unsafe_announcement.get("visibility").raise_error("Unknown visibility")
-                text = unsafe_announcement.get_string("text", 8192)
-                
-                if not unsafe_announcement.get_bool("is_enabled", True):
-                    continue
-                static_announcements.append({
-                    "type": type,
-                    "visibility": visibility,
-                    "text": text
-                })
-        
-        # Assign to global vars only if whole file parsed successfully
-        self._readonly = readonly
-        self._disabled = disable
-        self._static_announcements = static_announcements
+        self._data = LiveConfigData.from_json(json_config)
     
     def is_disabled(self) -> bool:
-        return self._disabled
+        return self._data.disabled
     
     def is_readonly(self) -> bool:
-        return self._readonly
+        return self._data.readonly
     
-    def get_static_announcements(self) -> list[dict]:
+    def get_static_announcements(self, is_mod: bool) -> list[dict]:
         """
-        Dicts contain (as in the api specification): type, visibility, text
+        Dicts contain (as in the api specification): type, page_visibility, text
         """
-        return self._static_announcements
+        return [
+            {
+                "type": announcement.type.to_json(),
+                "page_visibility": announcement.page_visibility.to_json(),
+                "text": announcement.text,
+            }
+            for announcement in self._data.static_announcements
+            if announcement.is_enabled and (is_mod or announcement.visible)
+        ]
 
 
 # Do not modify directly! Variable is imported by others
diff --git a/api/src/api/routes/site.py b/api/src/api/routes/site.py
index 928da6c155d260a3bc22875de6677b3d0d56eccc..0e3571df20849ca4f5249401224cff7a9acf5efe 100644
--- a/api/src/api/routes/site.py
+++ b/api/src/api/routes/site.py
@@ -25,7 +25,7 @@ def api_route_status():
     is_mod = is_moderator()
     status = "available"
     announcements = []
-    announcements.extend(api.live_config.get_static_announcements())
+    announcements.extend(api.live_config.get_static_announcements(is_mod))
 
     if api.live_config.is_disabled():
         status = "unavailable"
diff --git a/common_py/src/videoag_common/objects/site.py b/common_py/src/videoag_common/objects/site.py
index 39d65386f87f149ac050d7be017f17bc0ead3253..09bde4a031b4934dd61b2fab3a5797a56a507f77 100644
--- a/common_py/src/videoag_common/objects/site.py
+++ b/common_py/src/videoag_common/objects/site.py
@@ -8,13 +8,13 @@ from videoag_common.api_object import *
 from .course import Course, Lecture
 
 
-class AnnouncementType(Enum):
+class AnnouncementType(JsonSerializableEnum):
     INFO = "info"
     WARNING = "warning"
     IMPORTANT = "important"
 
 
-class AnnouncementPageVisibility(Enum):
+class AnnouncementPageVisibility(JsonSerializableEnum):
     ONLY_MAIN_PAGE = "only_main_page"
     ALL_PAGES = "all_pages"
     ALL_PAGES_AND_EMBED = "all_pages_and_embed"