diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h
--- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h
+++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h
@@ -9,10 +9,18 @@
 class NotificationsCryptoModule {
   const static std::string notificationsCryptoAccountID;
 
-  // Used for migration of legacy notifications sessions
+  // Used for handling of legacy notifications sessions
+  const static std::string secureStoreNotificationsAccountDataKey;
+  const static std::string keyserverHostedNotificationsID;
+
   static std::unique_ptr<crypto::CryptoModule> deserializeCryptoModule(
       const std::string &path,
       const std::string &picklingKey);
+  static void serializeAndFlushCryptoModule(
+      std::unique_ptr<crypto::CryptoModule> cryptoModule,
+      const std::string &path,
+      const std::string &picklingKey);
+
   static std::string
   getKeyserverNotificationsSessionKey(const std::string &keyserverID);
   static std::string serializeNotificationsSession(
@@ -38,34 +46,72 @@
       const std::string &keyserverID,
       std::shared_ptr<crypto::Session> keyserverNotificationsSession);
   static bool isNotificationsSessionInitialized(const std::string &keyserverID);
-  static std::string decrypt(
-      const std::string &keyserverID,
-      const std::string &data,
-      const size_t messageType);
 
-  class StatefulDecryptResult {
+  class BaseStatefulDecryptResult {
+    BaseStatefulDecryptResult(
+        std::string picklingKey,
+        std::string decryptedData);
+
+    std::string picklingKey;
+    std::string decryptedData;
+    friend NotificationsCryptoModule;
+
+  public:
+    std::string getDecryptedData();
+    std::string getPicklingKey();
+    virtual void flushState() = 0;
+    virtual ~BaseStatefulDecryptResult() = default;
+  };
+
+  class StatefulDecryptResult : public BaseStatefulDecryptResult {
     StatefulDecryptResult(
         std::unique_ptr<crypto::Session> session,
         std::string keyserverID,
         std::string picklingKey,
         std::string decryptedData);
+
     std::unique_ptr<crypto::Session> sessionState;
     std::string keyserverID;
-    std::string picklingKey;
-    std::string decryptedData;
     friend NotificationsCryptoModule;
 
   public:
-    std::string getDecryptedData();
     std::string getKeyserverID();
-    std::string getPicklingKey();
+    void flushState() override;
+  };
+
+  class LegacyStatefulDecryptResult : public BaseStatefulDecryptResult {
+    LegacyStatefulDecryptResult(
+        std::unique_ptr<crypto::CryptoModule> cryptoModule,
+        std::string path,
+        std::string picklingKey,
+        std::string decryptedData);
+    std::unique_ptr<crypto::CryptoModule> cryptoModule;
+    std::string path;
+    friend NotificationsCryptoModule;
+
+  public:
+    std::string getPath();
+    void flushState() override;
   };
 
-  static std::unique_ptr<StatefulDecryptResult> statefulDecrypt(
+private:
+  static std::unique_ptr<NotificationsCryptoModule::BaseStatefulDecryptResult>
+  prepareLegacyDecryptedState(
+      const std::string &data,
+      const size_t messageType);
+
+public:
+  static std::string decrypt(
       const std::string &keyserverID,
       const std::string &data,
       const size_t messageType);
+
+  static std::unique_ptr<BaseStatefulDecryptResult> statefulDecrypt(
+      const std::string &keyserverID,
+      const std::string &data,
+      const size_t messageType);
+
   static void
-  flushState(std::unique_ptr<StatefulDecryptResult> statefulDecryptResult);
+  flushState(std::unique_ptr<BaseStatefulDecryptResult> statefulDecryptResult);
 };
 } // namespace comm
diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp
--- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp
+++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp
@@ -6,17 +6,23 @@
 #include "../../Tools/Logger.h"
 #include "../../Tools/PlatformSpecificTools.h"
 
+#include <fcntl.h>
 #include <folly/String.h>
 #include <folly/dynamic.h>
 #include <folly/json.h>
+#include <unistd.h>
 #include <fstream>
 #include <memory>
 #include <sstream>
 
 namespace comm {
-
+const std::string
+    NotificationsCryptoModule::secureStoreNotificationsAccountDataKey =
+        "notificationsCryptoAccountDataKey";
 const std::string NotificationsCryptoModule::notificationsCryptoAccountID =
     "notificationsCryptoAccountDataID";
+const std::string NotificationsCryptoModule::keyserverHostedNotificationsID =
+    "keyserverHostedNotificationsID";
 const std::string NotificationsCryptoModule::initialEncryptedMessageContent =
     "{\"type\": \"init\"}";
 const int NotificationsCryptoModule::olmEncryptedTypeMessage = 1;
@@ -29,6 +35,7 @@
 // app version. Do not introduce new usages of this constant in the code!!!
 const std::string ashoatKeyserverIDUsedOnlyForMigrationFromLegacyNotifStorage =
     "256";
+const int temporaryFilePathRandomSuffixLength = 32;
 
 std::unique_ptr<crypto::CryptoModule>
 NotificationsCryptoModule::deserializeCryptoModule(
@@ -77,6 +84,68 @@
       crypto::Persist({account, sessions}));
 }
 
+void NotificationsCryptoModule::serializeAndFlushCryptoModule(
+    std::unique_ptr<crypto::CryptoModule> cryptoModule,
+    const std::string &path,
+    const std::string &picklingKey) {
+  crypto::Persist persist = cryptoModule->storeAsB64(picklingKey);
+
+  folly::dynamic sessions = folly::dynamic::object;
+  for (auto &sessionKeyValuePair : persist.sessions) {
+    std::string targetUserID = sessionKeyValuePair.first;
+    crypto::OlmBuffer sessionData = sessionKeyValuePair.second;
+    sessions[targetUserID] =
+        std::string(sessionData.begin(), sessionData.end());
+  }
+
+  std::string account =
+      std::string(persist.account.begin(), persist.account.end());
+  folly::dynamic persistJSON =
+      folly::dynamic::object("account", account)("sessions", sessions);
+  std::string pickledPersist = folly::toJson(persistJSON);
+
+  std::string temporaryFilePathRandomSuffix =
+      crypto::Tools::generateRandomHexString(
+          temporaryFilePathRandomSuffixLength);
+  std::string temporaryPath = path + temporaryFilePathRandomSuffix;
+
+  mode_t readWritePermissionsMode = 0666;
+  int temporaryFD =
+      open(temporaryPath.c_str(), O_CREAT | O_WRONLY, readWritePermissionsMode);
+  if (temporaryFD == -1) {
+    throw std::runtime_error(
+        "Failed to create temporary file. Unable to atomically update "
+        "notifications crypto account. Details: " +
+        std::string(strerror(errno)));
+  }
+  ssize_t bytesWritten =
+      write(temporaryFD, pickledPersist.c_str(), pickledPersist.length());
+  if (bytesWritten == -1 || bytesWritten != pickledPersist.length()) {
+    remove(temporaryPath.c_str());
+    throw std::runtime_error(
+        "Failed to write all data to temporary file. Unable to atomically "
+        "update notifications crypto account. Details: " +
+        std::string(strerror(errno)));
+  }
+  if (fsync(temporaryFD) == -1) {
+    remove(temporaryPath.c_str());
+    throw std::runtime_error(
+        "Failed to synchronize temporary file data with hardware storage. "
+        "Unable to atomically update notifications crypto account. Details: " +
+        std::string(strerror(errno)));
+  };
+  close(temporaryFD);
+  if (rename(temporaryPath.c_str(), path.c_str()) == -1) {
+    remove(temporaryPath.c_str());
+    throw std::runtime_error(
+        "Failed to replace temporary file content with notifications crypto "
+        "account. Unable to atomically update notifications crypto account. "
+        "Details: " +
+        std::string(strerror(errno)));
+  }
+  remove(temporaryPath.c_str());
+}
+
 std::string NotificationsCryptoModule::getKeyserverNotificationsSessionKey(
     const std::string &keyserverID) {
   return "KEYSERVER." + keyserverID + ".NOTIFS_SESSION";
@@ -214,21 +283,20 @@
   return CommMMKV::getString(keyserverNotificationsSessionKey).has_value();
 }
 
-std::string NotificationsCryptoModule::decrypt(
-    const std::string &keyserverID,
-    const std::string &data,
-    const size_t messageType) {
-  std::unique_ptr<crypto::Session> session;
-  std::string picklingKey;
-  std::tie(session, picklingKey) =
-      NotificationsCryptoModule::fetchNotificationsSession(keyserverID);
+NotificationsCryptoModule::BaseStatefulDecryptResult::BaseStatefulDecryptResult(
+    std::string picklingKey,
+    std::string decryptedData)
+    : picklingKey(picklingKey), decryptedData(decryptedData) {
+}
 
-  crypto::EncryptedData encryptedData{
-      std::vector<uint8_t>(data.begin(), data.end()), messageType};
-  std::string decryptedData = session->decrypt(encryptedData);
-  NotificationsCryptoModule::persistNotificationsSessionInternal(
-      keyserverID, picklingKey, std::move(session));
-  return decryptedData;
+std::string
+NotificationsCryptoModule::BaseStatefulDecryptResult::getDecryptedData() {
+  return this->decryptedData;
+}
+
+std::string
+NotificationsCryptoModule::BaseStatefulDecryptResult::getPicklingKey() {
+  return this->picklingKey;
 }
 
 NotificationsCryptoModule::StatefulDecryptResult::StatefulDecryptResult(
@@ -236,50 +304,133 @@
     std::string keyserverID,
     std::string picklingKey,
     std::string decryptedData)
-    : sessionState(std::move(session)),
-      keyserverID(keyserverID),
-      picklingKey(picklingKey),
-      decryptedData(decryptedData) {
-}
-
-std::string
-NotificationsCryptoModule::StatefulDecryptResult::getDecryptedData() {
-  return this->decryptedData;
+    : NotificationsCryptoModule::BaseStatefulDecryptResult::
+          BaseStatefulDecryptResult(picklingKey, decryptedData),
+      sessionState(std::move(session)),
+      keyserverID(keyserverID) {
 }
 
 std::string NotificationsCryptoModule::StatefulDecryptResult::getKeyserverID() {
   return this->keyserverID;
 }
 
-std::string NotificationsCryptoModule::StatefulDecryptResult::getPicklingKey() {
-  return this->picklingKey;
+void NotificationsCryptoModule::StatefulDecryptResult::flushState() {
+  NotificationsCryptoModule::persistNotificationsSessionInternal(
+      this->getKeyserverID(),
+      this->getPicklingKey(),
+      std::move(this->sessionState));
 }
 
-std::unique_ptr<NotificationsCryptoModule::StatefulDecryptResult>
-NotificationsCryptoModule::statefulDecrypt(
+NotificationsCryptoModule::LegacyStatefulDecryptResult::
+    LegacyStatefulDecryptResult(
+        std::unique_ptr<crypto::CryptoModule> cryptoModule,
+        std::string path,
+        std::string picklingKey,
+        std::string decryptedData)
+    : NotificationsCryptoModule::BaseStatefulDecryptResult::
+          BaseStatefulDecryptResult(picklingKey, decryptedData),
+      path(path),
+      cryptoModule(std::move(cryptoModule)) {
+}
+
+std::string NotificationsCryptoModule::LegacyStatefulDecryptResult::getPath() {
+  return this->path;
+}
+
+void NotificationsCryptoModule::LegacyStatefulDecryptResult::flushState() {
+  NotificationsCryptoModule::serializeAndFlushCryptoModule(
+      std::move(this->cryptoModule), this->getPath(), this->getPicklingKey());
+}
+
+std::unique_ptr<NotificationsCryptoModule::BaseStatefulDecryptResult>
+NotificationsCryptoModule::prepareLegacyDecryptedState(
+    const std::string &data,
+    const size_t messageType) {
+  folly::Optional<std::string> picklingKey = comm::CommSecureStore::get(
+      NotificationsCryptoModule::secureStoreNotificationsAccountDataKey);
+
+  if (!picklingKey.hasValue()) {
+    throw std::runtime_error(
+        "Legacy notifications session pickling key missing.");
+  }
+
+  std::string legacyNotificationsAccountPath =
+      comm::PlatformSpecificTools::getNotificationsCryptoAccountPath();
+
+  crypto::EncryptedData encryptedData{
+      std::vector<uint8_t>(data.begin(), data.end()), messageType};
+
+  auto cryptoModule = NotificationsCryptoModule::deserializeCryptoModule(
+      legacyNotificationsAccountPath, picklingKey.value());
+
+  std::string decryptedData = cryptoModule->decrypt(
+      NotificationsCryptoModule::keyserverHostedNotificationsID, encryptedData);
+
+  LegacyStatefulDecryptResult statefulDecryptResult(
+      std::move(cryptoModule),
+      legacyNotificationsAccountPath,
+      picklingKey.value(),
+      decryptedData);
+
+  return std::make_unique<LegacyStatefulDecryptResult>(
+      std::move(statefulDecryptResult));
+}
+
+std::string NotificationsCryptoModule::decrypt(
     const std::string &keyserverID,
     const std::string &data,
     const size_t messageType) {
   std::unique_ptr<crypto::Session> session;
   std::string picklingKey;
-  std::tie(session, picklingKey) =
-      NotificationsCryptoModule::fetchNotificationsSession(keyserverID);
 
   crypto::EncryptedData encryptedData{
       std::vector<uint8_t>(data.begin(), data.end()), messageType};
+
+  try {
+    std::tie(session, picklingKey) =
+        NotificationsCryptoModule::fetchNotificationsSession(keyserverID);
+  } catch (const std::exception &e) {
+    auto statefulDecryptResult =
+        NotificationsCryptoModule::prepareLegacyDecryptedState(
+            data, messageType);
+    statefulDecryptResult->flushState();
+    return statefulDecryptResult->getDecryptedData();
+  }
+
   std::string decryptedData = session->decrypt(encryptedData);
+  NotificationsCryptoModule::persistNotificationsSessionInternal(
+      keyserverID, picklingKey, std::move(session));
+  return decryptedData;
+}
+
+std::unique_ptr<NotificationsCryptoModule::BaseStatefulDecryptResult>
+NotificationsCryptoModule::statefulDecrypt(
+    const std::string &keyserverID,
+    const std::string &data,
+    const size_t messageType) {
+  std::unique_ptr<crypto::Session> session;
+  std::string picklingKey;
+  crypto::EncryptedData encryptedData{
+      std::vector<uint8_t>(data.begin(), data.end()), messageType};
 
+  try {
+    std::tie(session, picklingKey) =
+        NotificationsCryptoModule::fetchNotificationsSession(keyserverID);
+  } catch (const std::exception &e) {
+    return NotificationsCryptoModule::prepareLegacyDecryptedState(
+        data, messageType);
+  }
+
+  std::string decryptedData = session->decrypt(encryptedData);
   StatefulDecryptResult statefulDecryptResult(
       std::move(session), keyserverID, picklingKey, decryptedData);
+
   return std::make_unique<StatefulDecryptResult>(
       std::move(statefulDecryptResult));
 }
 
 void NotificationsCryptoModule::flushState(
-    std::unique_ptr<StatefulDecryptResult> statefulDecryptResult) {
-  NotificationsCryptoModule::persistNotificationsSessionInternal(
-      statefulDecryptResult->getKeyserverID(),
-      statefulDecryptResult->getPicklingKey(),
-      std::move(statefulDecryptResult->sessionState));
+    std::unique_ptr<BaseStatefulDecryptResult> baseStatefulDecryptResult) {
+  baseStatefulDecryptResult->flushState();
 }
 } // namespace comm
diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm
--- a/native/ios/NotificationService/NotificationService.mm
+++ b/native/ios/NotificationService/NotificationService.mm
@@ -85,7 +85,7 @@
   UNNotificationContent *publicUserContent = content;
 
   // Step 1: notification decryption.
-  std::unique_ptr<comm::NotificationsCryptoModule::StatefulDecryptResult>
+  std::unique_ptr<comm::NotificationsCryptoModule::BaseStatefulDecryptResult>
       statefulDecryptResultPtr;
   BOOL decryptionExecuted = NO;
 
@@ -178,7 +178,8 @@
           " during unread count calculation.";
     }
 
-    if (unreadCountCalculationError.size()) {
+    if (unreadCountCalculationError.size() &&
+        comm::StaffUtils::isStaffRelease()) {
       [errorMessages
           addObject:[NSString stringWithUTF8String:unreadCountCalculationError
                                                        .c_str()]];
@@ -544,7 +545,7 @@
       [payload[encryptionFailureKey] isEqualToNumber:@(1)];
 }
 
-- (std::unique_ptr<comm::NotificationsCryptoModule::StatefulDecryptResult>)
+- (std::unique_ptr<comm::NotificationsCryptoModule::BaseStatefulDecryptResult>)
     decryptContentInPlace:(UNMutableNotificationContent *)content {
   std::string encryptedData =
       std::string([content.userInfo[encryptedPayloadKey] UTF8String]);