diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js
index 5c23f0403..c5ec79830 100644
--- a/lib/types/sqlite-types.js
+++ b/lib/types/sqlite-types.js
@@ -1,85 +1,89 @@
 // @flow
 
 import type { ClientDBMessageInfo } from './message-types.js';
 import type { StoreOperations } from './store-ops-types.js';
+import type { ClientDBDMOperation } from '../ops/dm-operations-store-ops.js';
 
 export const outboundP2PMessageStatuses = Object.freeze({
   // The message was prepared to be sent to other peers, but it's not encrypted.
   // It was inserted into DB in the same transaction as making changes to
   // the store.
   persisted: 'persisted',
   // Encryption is done in the same transaction as persisting the CryptoModule,
   // and message order is also tracked on the client side,
   // which means the message can be sent.
   encrypted: 'encrypted',
   // The message was sent to another peer (Tunnelbroker owns it),
   // waiting for the confirmation (handled in `peerToPeerMessageHandler`).
   sent: 'sent',
 });
 export type OutboundP2PMessageStatuses = $Values<
   typeof outboundP2PMessageStatuses,
 >;
 
 export type InboundP2PMessage = {
   +messageID: string,
   +senderDeviceID: string,
   +plaintext: string,
   +status: string,
   +senderUserID: string,
 };
 
 export type OutboundP2PMessage = {
   +messageID: string,
   +deviceID: string,
   +userID: string,
   +timestamp: string,
   +plaintext: string,
   +ciphertext: string,
   +status: OutboundP2PMessageStatuses,
   +supportsAutoRetry: boolean,
 };
 
 export type SQLiteAPI = {
   // read operations
   +getAllInboundP2PMessages: () => Promise<Array<InboundP2PMessage>>,
   +getInboundP2PMessagesByID: (
     ids: $ReadOnlyArray<string>,
   ) => Promise<Array<InboundP2PMessage>>,
   +getUnsentOutboundP2PMessages: () => Promise<Array<OutboundP2PMessage>>,
   +getRelatedMessages: (
     messageID: string,
   ) => Promise<Array<ClientDBMessageInfo>>,
   +getOutboundP2PMessagesByID: (
     ids: $ReadOnlyArray<string>,
   ) => Promise<Array<OutboundP2PMessage>>,
   +searchMessages: (
     query: string,
     threadID: string,
     timestampCursor: ?string,
     messageIDCursor: ?string,
   ) => Promise<Array<ClientDBMessageInfo>>,
   +fetchMessages: (
     threadID: string,
     limit: number,
     offset: number,
   ) => Promise<Array<ClientDBMessageInfo>>,
+  +fetchDMOperationsByType: (
+    type: string,
+  ) => Promise<Array<ClientDBDMOperation>>,
 
   // write operations
   +removeInboundP2PMessages: (ids: $ReadOnlyArray<string>) => Promise<void>,
   +markOutboundP2PMessageAsSent: (
     messageID: string,
     deviceID: string,
   ) => Promise<void>,
   +resetOutboundP2PMessagesForDevice: (
     deviceID: string,
   ) => Promise<Array<string>>,
   +removeOutboundP2PMessage: (
     messageID: string,
     deviceID: string,
   ) => Promise<void>,
 
   +processDBStoreOperations: (
     operations: StoreOperations,
     userID?: ?string,
   ) => Promise<void>,
 };
diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js
index bd692154e..cbeb79b61 100644
--- a/lib/utils/__mocks__/config.js
+++ b/lib/utils/__mocks__/config.js
@@ -1,65 +1,66 @@
 // @flow
 
 import { type Config } from '../config.js';
 
 const getConfig = (): Config => ({
   resolveKeyserverSessionInvalidationUsingNativeCredentials: null,
   setSessionIDOnRequest: true,
   calendarRangeInactivityLimit: null,
   platformDetails: {
     platform: 'web',
     codeVersion: 70,
     stateVersion: 50,
   },
   authoritativeKeyserverID: '123',
   olmAPI: {
     initializeCryptoAccount: jest.fn(),
     getUserPublicKey: jest.fn(),
     encrypt: jest.fn(),
     encryptAndPersist: jest.fn(),
     encryptNotification: jest.fn(),
     decrypt: jest.fn(),
     decryptAndPersist: jest.fn(),
     contentInboundSessionCreator: jest.fn(),
     contentOutboundSessionCreator: jest.fn(),
     keyserverNotificationsSessionCreator: jest.fn(),
     notificationsOutboundSessionCreator: jest.fn(),
     isContentSessionInitialized: jest.fn(),
     isDeviceNotificationsSessionInitialized: jest.fn(),
     isNotificationsSessionInitializedWithDevices: jest.fn(),
     getOneTimeKeys: jest.fn(),
     validateAndUploadPrekeys: jest.fn(),
     signMessage: jest.fn(),
     verifyMessage: jest.fn(),
     markPrekeysAsPublished: jest.fn(),
   },
   sqliteAPI: {
     getAllInboundP2PMessages: jest.fn(),
     getInboundP2PMessagesByID: jest.fn(),
     removeInboundP2PMessages: jest.fn(),
     processDBStoreOperations: jest.fn(),
     getUnsentOutboundP2PMessages: jest.fn(),
     markOutboundP2PMessageAsSent: jest.fn(),
     removeOutboundP2PMessage: jest.fn(),
     resetOutboundP2PMessagesForDevice: jest.fn(),
     getRelatedMessages: jest.fn(),
     getOutboundP2PMessagesByID: jest.fn(),
     searchMessages: jest.fn(),
     fetchMessages: jest.fn(),
+    fetchDMOperationsByType: jest.fn(),
   },
   encryptedNotifUtilsAPI: {
     generateAESKey: jest.fn(),
     encryptWithAESKey: jest.fn(),
     encryptSerializedNotifPayload: jest.fn(),
     uploadLargeNotifPayload: jest.fn(),
     getEncryptedNotifHash: jest.fn(),
     getBlobHash: jest.fn(),
     getNotifByteSize: jest.fn(),
     normalizeUint8ArrayForBlobUpload: jest.fn(),
   },
   showAlert: jest.fn(),
 });
 
 const hasConfig = (): boolean => true;
 
 export { getConfig, hasConfig };
diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js
index 27c97c838..b87e6a8b2 100644
--- a/native/database/sqlite-api.js
+++ b/native/database/sqlite-api.js
@@ -1,65 +1,66 @@
 // @flow
 
 import { getKeyserversToRemoveFromNotifsStore } from 'lib/ops/keyserver-store-ops.js';
 import { convertStoreOperationsToClientDBStoreOperations } from 'lib/shared/redux/client-db-utils.js';
 import type { SQLiteAPI } from 'lib/types/sqlite-types.js';
 import type { StoreOperations } from 'lib/types/store-ops-types';
 import { values } from 'lib/utils/objects.js';
 
 import { commCoreModule } from '../native-modules.js';
 import { isTaskCancelledError } from '../utils/error-handling.js';
 
 const sqliteAPI: SQLiteAPI = {
   // read operations
   getAllInboundP2PMessages: commCoreModule.getAllInboundP2PMessages,
   getInboundP2PMessagesByID: commCoreModule.getInboundP2PMessagesByID,
   getUnsentOutboundP2PMessages: commCoreModule.getUnsentOutboundP2PMessages,
   getRelatedMessages: commCoreModule.getRelatedMessages,
   getOutboundP2PMessagesByID: commCoreModule.getOutboundP2PMessagesByID,
   searchMessages: commCoreModule.searchMessages,
   fetchMessages: commCoreModule.fetchMessages,
+  fetchDMOperationsByType: commCoreModule.getDMOperationsByType,
 
   // write operations
   removeInboundP2PMessages: commCoreModule.removeInboundP2PMessages,
   markOutboundP2PMessageAsSent: commCoreModule.markOutboundP2PMessageAsSent,
   resetOutboundP2PMessagesForDevice:
     commCoreModule.resetOutboundP2PMessagesForDevice,
   removeOutboundP2PMessage: commCoreModule.removeOutboundP2PMessage,
 
   async processDBStoreOperations(
     storeOperations: StoreOperations,
   ): Promise<void> {
     const keyserversToRemoveFromNotifsStore =
       getKeyserversToRemoveFromNotifsStore(
         storeOperations.keyserverStoreOperations ?? [],
       );
 
     try {
       const promises = [];
       if (keyserversToRemoveFromNotifsStore.length > 0) {
         promises.push(
           commCoreModule.removeKeyserverDataFromNotifStorage(
             keyserversToRemoveFromNotifsStore,
           ),
         );
       }
 
       const dbOps =
         convertStoreOperationsToClientDBStoreOperations(storeOperations);
       if (values(dbOps).some(ops => ops && ops.length > 0)) {
         promises.push(commCoreModule.processDBStoreOperations(dbOps));
       }
       await Promise.all(promises);
     } catch (e) {
       if (isTaskCancelledError(e)) {
         return;
       }
       // this code will make an entry in SecureStore and cause re-creating
       // database when user will open app again
       commCoreModule.reportDBOperationsFailure();
       commCoreModule.terminate();
     }
   },
 };
 
 export { sqliteAPI };
diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js
index ca30179c3..db25b778c 100644
--- a/web/database/sqlite-api.js
+++ b/web/database/sqlite-api.js
@@ -1,201 +1,215 @@
 // @flow
 
+import type { ClientDBDMOperation } from 'lib/ops/dm-operations-store-ops.js';
 import { convertStoreOperationsToClientDBStoreOperations } from 'lib/shared/redux/client-db-utils.js';
 import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
 import type {
   SQLiteAPI,
   InboundP2PMessage,
   OutboundP2PMessage,
 } from 'lib/types/sqlite-types.js';
 import type { StoreOperations } from 'lib/types/store-ops-types.js';
 import { entries, values } from 'lib/utils/objects.js';
 
 import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js';
 import { workerRequestMessageTypes } from '../types/worker-types.js';
 
 const sqliteAPI: SQLiteAPI = {
   // read operations
   async getAllInboundP2PMessages(): Promise<InboundP2PMessage[]> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES,
     });
     const messages: ?$ReadOnlyArray<InboundP2PMessage> =
       data?.inboundP2PMessages;
     return messages ? [...messages] : [];
   },
 
   async getInboundP2PMessagesByID(
     ids: $ReadOnlyArray<string>,
   ): Promise<Array<InboundP2PMessage>> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES_BY_ID,
       messageIDs: ids,
     });
     const messages: ?$ReadOnlyArray<InboundP2PMessage> =
       data?.inboundP2PMessages;
     return messages ? [...messages] : [];
   },
 
   async getUnsentOutboundP2PMessages(): Promise<OutboundP2PMessage[]> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES,
     });
     const messages: ?$ReadOnlyArray<OutboundP2PMessage> =
       data?.outboundP2PMessages;
     return messages ? [...messages] : [];
   },
 
   async getRelatedMessages(messageID: string): Promise<ClientDBMessageInfo[]> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.GET_RELATED_MESSAGES,
       messageID,
     });
     const messages: ?$ReadOnlyArray<ClientDBMessageInfo> = data?.messages;
     return messages ? [...messages] : [];
   },
 
   async getOutboundP2PMessagesByID(
     ids: $ReadOnlyArray<string>,
   ): Promise<Array<OutboundP2PMessage>> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES_BY_ID,
       messageIDs: ids,
     });
     const messages: ?$ReadOnlyArray<OutboundP2PMessage> =
       data?.outboundP2PMessages;
     return messages ? [...messages] : [];
   },
 
   async searchMessages(
     query: string,
     threadID: string,
     timestampCursor: ?string,
     messageIDCursor: ?string,
   ): Promise<ClientDBMessageInfo[]> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.SEARCH_MESSAGES,
       query,
       threadID,
       timestampCursor,
       messageIDCursor,
     });
     const messages: ?$ReadOnlyArray<ClientDBMessageInfo> = data?.messages;
     return messages ? [...messages] : [];
   },
 
   async fetchMessages(
     threadID: string,
     limit: number,
     offset: number,
   ): Promise<ClientDBMessageInfo[]> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.FETCH_MESSAGES,
       threadID,
       limit,
       offset,
     });
     const messages: ?$ReadOnlyArray<ClientDBMessageInfo> = data?.messages;
     return messages ? [...messages] : [];
   },
 
+  async fetchDMOperationsByType(
+    type: string,
+  ): Promise<Array<ClientDBDMOperation>> {
+    const sharedWorker = await getCommSharedWorker();
+
+    const data = await sharedWorker.schedule({
+      type: workerRequestMessageTypes.GET_DM_OPERATIONS_BY_TYPE,
+      operationType: type,
+    });
+    const operations = data?.operations;
+    return operations ? [...operations] : [];
+  },
+
   // write operations
   async removeInboundP2PMessages(ids: $ReadOnlyArray<string>): Promise<void> {
     const sharedWorker = await getCommSharedWorker();
 
     await sharedWorker.schedule({
       type: workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES,
       ids,
     });
   },
 
   async markOutboundP2PMessageAsSent(
     messageID: string,
     deviceID: string,
   ): Promise<void> {
     const sharedWorker = await getCommSharedWorker();
 
     await sharedWorker.schedule({
       type: workerRequestMessageTypes.MARK_OUTBOUND_P2P_MESSAGE_AS_SENT,
       messageID,
       deviceID,
     });
   },
 
   async resetOutboundP2PMessagesForDevice(
     deviceID: string,
   ): Promise<Array<string>> {
     const sharedWorker = await getCommSharedWorker();
 
     const data = await sharedWorker.schedule({
       type: workerRequestMessageTypes.RESET_OUTBOUND_P2P_MESSAGES,
       deviceID,
     });
     const messageIDs: ?$ReadOnlyArray<string> = data?.messageIDs;
     return messageIDs ? [...messageIDs] : [];
   },
 
   async removeOutboundP2PMessage(
     messageID: string,
     deviceID: string,
   ): Promise<void> {
     const sharedWorker = await getCommSharedWorker();
 
     await sharedWorker.schedule({
       type: workerRequestMessageTypes.REMOVE_OUTBOUND_P2P_MESSAGE,
       messageID,
       deviceID,
     });
   },
 
   async processDBStoreOperations(
     storeOperations: StoreOperations,
   ): Promise<void> {
     const dbOps =
       convertStoreOperationsToClientDBStoreOperations(storeOperations);
 
     if (!values(dbOps).some(ops => ops && ops.length > 0)) {
       return;
     }
 
     const sharedWorker = await getCommSharedWorker();
     const isSupported = await sharedWorker.isSupported();
     if (!isSupported) {
       return;
     }
     try {
       await sharedWorker.schedule({
         type: workerRequestMessageTypes.PROCESS_STORE_OPERATIONS,
         storeOperations: dbOps,
       });
     } catch (e) {
       console.log(e);
       if (
         entries(storeOperations).some(
           ([key, ops]) =>
             key !== 'draftStoreOperations' &&
             key !== 'reportStoreOperations' &&
             ops.length > 0,
         )
       ) {
         await sharedWorker.init({ clearDatabase: true, markAsCorrupted: true });
         location.reload();
       }
     }
   },
 };
 
 export { sqliteAPI };