diff --git a/lib/components/prekeys-handler.react.js b/lib/components/prekeys-handler.react.js
--- a/lib/components/prekeys-handler.react.js
+++ b/lib/components/prekeys-handler.react.js
@@ -24,8 +24,10 @@
 
     const timeoutID = setTimeout(async () => {
       try {
+        const authMetadata = await identityContext.getAuthMetadata();
+
         const { olmAPI } = getConfig();
-        await olmAPI.validateAndUploadPrekeys(identityContext);
+        await olmAPI.validateAndUploadPrekeys(authMetadata);
       } catch (e) {
         console.log('Prekey validation error: ', e.message);
       }
diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js
--- a/lib/types/crypto-types.js
+++ b/lib/types/crypto-types.js
@@ -2,7 +2,7 @@
 
 import t, { type TInterface } from 'tcomb';
 
-import { type IdentityClientContextType } from '../shared/identity-client-context.js';
+import { type AuthMetadata } from '../shared/identity-client-context.js';
 import { tShape } from '../utils/validation-utils.js';
 
 export type OLMIdentityKeys = {
@@ -131,7 +131,5 @@
     initialEncryptedContent: string,
   ) => Promise<string>,
   +getOneTimeKeys: (numberOfKeys: number) => Promise<OneTimeKeysResultValues>,
-  +validateAndUploadPrekeys: (
-    identityContext: IdentityClientContextType,
-  ) => Promise<void>,
+  +validateAndUploadPrekeys: (authMetadata: AuthMetadata) => Promise<void>,
 };
diff --git a/native/crypto/olm-api.js b/native/crypto/olm-api.js
--- a/native/crypto/olm-api.js
+++ b/native/crypto/olm-api.js
@@ -1,7 +1,7 @@
 // @flow
 
 import { getOneTimeKeyValues } from 'lib/shared/crypto-utils.js';
-import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js';
+import { type AuthMetadata } from 'lib/shared/identity-client-context.js';
 import type {
   OneTimeKeysResultValues,
   OlmAPI,
@@ -38,15 +38,7 @@
       notificationsOneTimeKeys: getOneTimeKeyValues(notificationsOneTimeKeys),
     };
   },
-  async validateAndUploadPrekeys(
-    identityContext: IdentityClientContextType,
-  ): Promise<void> {
-    let authMetadata;
-    try {
-      authMetadata = await identityContext.getAuthMetadata();
-    } catch (e) {
-      return;
-    }
+  async validateAndUploadPrekeys(authMetadata: AuthMetadata): Promise<void> {
     const { userID, deviceID, accessToken } = authMetadata;
     if (!userID || !deviceID || !accessToken) {
       return;
diff --git a/web/crypto/olm-api.js b/web/crypto/olm-api.js
--- a/web/crypto/olm-api.js
+++ b/web/crypto/olm-api.js
@@ -1,43 +1,43 @@
 // @flow
 
 import olm from '@commapp/olm';
-import type { Account, Session } from '@commapp/olm';
 
-import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js';
-import {
-  type OlmAPI,
-  olmEncryptedMessageTypes,
-  type OLMIdentityKeys,
-  type OneTimeKeysResultValues,
-} from 'lib/types/crypto-types.js';
-import {
-  getAccountOneTimeKeys,
-  getAccountPrekeysSet,
-  shouldForgetPrekey,
-  shouldRotatePrekey,
-} from 'lib/utils/olm-utils.js';
+import { type OlmAPI } from 'lib/types/crypto-types.js';
 
 import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js';
 import { getOlmWasmPath } from '../shared-worker/utils/constants.js';
-import { workerRequestMessageTypes } from '../types/worker-types.js';
+import {
+  workerRequestMessageTypes,
+  workerResponseMessageTypes,
+} from '../types/worker-types.js';
 
 const usingSharedWorker = false;
-// methods below are just mocks to SQLite API
-// implement proper methods tracked in ENG-6462
 
-function getOlmAccount(): Account {
-  const account = new olm.Account();
-  account.create();
-  return account;
-}
-// eslint-disable-next-line no-unused-vars
-function getOlmSession(deviceID: string): Session {
-  return new olm.Session();
+function proxyToWorker<T>(
+  method: $Keys<OlmAPI>,
+): (...args: $ReadOnlyArray<mixed>) => Promise<T> {
+  return async (...args: $ReadOnlyArray<mixed>) => {
+    const sharedWorker = await getCommSharedWorker();
+    const result = await sharedWorker.schedule({
+      type: workerRequestMessageTypes.CALL_OLM_API_METHOD,
+      method,
+      args,
+    });
+
+    if (!result) {
+      throw new Error(`Worker OlmAPI call didn't return expected message`);
+    } else if (result.type !== workerResponseMessageTypes.CALL_OLM_API_METHOD) {
+      throw new Error(
+        `Worker OlmAPI call didn't return expected message. Instead got: ${JSON.stringify(
+          result,
+        )}`,
+      );
+    }
+
+    // Worker should return a message with the corresponding return type
+    return (result.result: any);
+  };
 }
-// eslint-disable-next-line no-unused-vars
-function storeOlmAccount(account: Account): void {}
-// eslint-disable-next-line no-unused-vars
-function storeOlmSession(session: Session): void {}
 
 const olmAPI: OlmAPI = {
   async initializeCryptoAccount(): Promise<void> {
@@ -51,105 +51,11 @@
       await olm.init();
     }
   },
-  async encrypt(content: string, deviceID: string): Promise<string> {
-    const session = getOlmSession(deviceID);
-    const { body } = session.encrypt(content);
-    storeOlmSession(session);
-    return body;
-  },
-  async decrypt(encryptedContent: string, deviceID: string): Promise<string> {
-    const session = getOlmSession(deviceID);
-    const result = session.decrypt(
-      olmEncryptedMessageTypes.TEXT,
-      encryptedContent,
-    );
-    storeOlmSession(session);
-    return result;
-  },
-  async contentInboundSessionCreator(
-    contentIdentityKeys: OLMIdentityKeys,
-    initialEncryptedContent: string,
-  ): Promise<string> {
-    const account = getOlmAccount();
-    const session = new olm.Session();
-    session.create_inbound_from(
-      account,
-      contentIdentityKeys.curve25519,
-      initialEncryptedContent,
-    );
-
-    account.remove_one_time_keys(session);
-    const initialEncryptedMessage = session.decrypt(
-      olmEncryptedMessageTypes.PREKEY,
-      initialEncryptedContent,
-    );
-    storeOlmAccount(account);
-    storeOlmSession(session);
-    return initialEncryptedMessage;
-  },
-  async getOneTimeKeys(numberOfKeys: number): Promise<OneTimeKeysResultValues> {
-    const contentAccount = getOlmAccount();
-    const notifAccount = getOlmAccount();
-    const contentOneTimeKeys = getAccountOneTimeKeys(
-      contentAccount,
-      numberOfKeys,
-    );
-    contentAccount.mark_keys_as_published();
-    storeOlmAccount(contentAccount);
-
-    const notificationsOneTimeKeys = getAccountOneTimeKeys(
-      notifAccount,
-      numberOfKeys,
-    );
-    notifAccount.mark_keys_as_published();
-    storeOlmAccount(notifAccount);
-
-    return { contentOneTimeKeys, notificationsOneTimeKeys };
-  },
-  async validateAndUploadPrekeys(
-    identityContext: IdentityClientContextType,
-  ): Promise<void> {
-    const authMetadata = await identityContext.getAuthMetadata();
-    const { userID, deviceID, accessToken } = authMetadata;
-    if (!userID || !deviceID || !accessToken) {
-      return;
-    }
-
-    const contentAccount = getOlmAccount();
-    if (shouldRotatePrekey(contentAccount)) {
-      contentAccount.generate_prekey();
-    }
-    if (shouldForgetPrekey(contentAccount)) {
-      contentAccount.forget_old_prekey();
-    }
-    await storeOlmAccount(contentAccount);
-
-    if (!contentAccount.unpublished_prekey()) {
-      return;
-    }
-
-    const notifAccount = getOlmAccount();
-    const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
-      getAccountPrekeysSet(notifAccount);
-    const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
-      getAccountPrekeysSet(contentAccount);
-
-    if (!notifPrekeySignature || !contentPrekeySignature) {
-      throw new Error('Prekey signature is missing');
-    }
-
-    if (!identityContext.identityClient.publishWebPrekeys) {
-      throw new Error('Publish prekeys method unimplemented');
-    }
-    await identityContext.identityClient.publishWebPrekeys({
-      contentPrekey,
-      contentPrekeySignature,
-      notifPrekey,
-      notifPrekeySignature,
-    });
-    contentAccount.mark_keys_as_published();
-    await storeOlmAccount(contentAccount);
-  },
+  encrypt: proxyToWorker('encrypt'),
+  decrypt: proxyToWorker('decrypt'),
+  contentInboundSessionCreator: proxyToWorker('contentInboundSessionCreator'),
+  getOneTimeKeys: proxyToWorker('getOneTimeKeys'),
+  validateAndUploadPrekeys: proxyToWorker('validateAndUploadPrekeys'),
 };
 
 export { olmAPI, usingSharedWorker };
diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js
--- a/web/shared-worker/worker/shared-worker.js
+++ b/web/shared-worker/worker/shared-worker.js
@@ -253,7 +253,7 @@
 
   let result;
   if (isOlmAPIRequest) {
-    await processAppOlmApiRequest(message);
+    result = await processAppOlmApiRequest(message);
   } else if (isIdentityClientRequest) {
     result = await processAppIdentityClientRequest(
       sqliteQueryExecutor,
diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js
--- a/web/shared-worker/worker/worker-crypto.js
+++ b/web/shared-worker/worker/worker-crypto.js
@@ -3,11 +3,15 @@
 import olm from '@commapp/olm';
 import uuid from 'uuid';
 
-import type {
-  CryptoStore,
-  PickledOLMAccount,
-  IdentityKeysBlob,
-  SignedIdentityKeysBlob,
+import {
+  olmEncryptedMessageTypes,
+  type OLMIdentityKeys,
+  type CryptoStore,
+  type PickledOLMAccount,
+  type IdentityKeysBlob,
+  type SignedIdentityKeysBlob,
+  type OlmAPI,
+  type OneTimeKeysResultValues,
 } from 'lib/types/crypto-types.js';
 import type {
   IdentityNewDeviceKeyUpload,
@@ -15,16 +19,22 @@
 } from 'lib/types/identity-service-types.js';
 import { entries } from 'lib/utils/objects.js';
 import {
-  retrieveIdentityKeysAndPrekeys,
   retrieveAccountKeysSet,
+  getAccountOneTimeKeys,
+  getAccountPrekeysSet,
+  shouldForgetPrekey,
+  shouldRotatePrekey,
+  retrieveIdentityKeysAndPrekeys,
 } from 'lib/utils/olm-utils.js';
 
+import { getIdentityClient } from './identity-client.js';
 import { getProcessingStoreOpsExceptionMessage } from './process-operations.js';
 import { getDBModule, getSQLiteQueryExecutor } from './worker-database.js';
 import {
   type WorkerRequestMessage,
   type WorkerResponseMessage,
   workerRequestMessageTypes,
+  workerResponseMessageTypes,
 } from '../../types/worker-types.js';
 import type { OlmPersistSession } from '../types/sqlite-query-executor.js';
 
@@ -194,23 +204,7 @@
     return;
   }
 
-  const contentAccountResult = getOrCreateOlmAccount(
-    sqliteQueryExecutor.getContentAccountID(),
-  );
-  const contentSessions = getOlmSessions(contentAccountResult.picklingKey);
-  const notificationAccountResult = getOrCreateOlmAccount(
-    sqliteQueryExecutor.getNotifsAccountID(),
-  );
-
-  cryptoStore = {
-    contentAccountPickleKey: contentAccountResult.picklingKey,
-    contentAccount: contentAccountResult.account,
-    contentSessions,
-    notificationAccountPickleKey: notificationAccountResult.picklingKey,
-    notificationAccount: notificationAccountResult.account,
-  };
-
-  persistCryptoStore();
+  await olmAPI.initializeCryptoAccount();
 }
 
 async function processAppOlmApiRequest(
@@ -221,7 +215,19 @@
       message.olmWasmPath,
       message.initialCryptoStore,
     );
+  } else if (message.type === workerRequestMessageTypes.CALL_OLM_API_METHOD) {
+    const method: (...$ReadOnlyArray<mixed>) => mixed = (olmAPI[
+      message.method
+    ]: any);
+    // Flow doesn't allow us to bind the (stringified) method name with
+    // the argument types so we need to pass the args as mixed.
+    const result = await method(...message.args);
+    return {
+      type: workerResponseMessageTypes.CALL_OLM_API_METHOD,
+      result,
+    };
   }
+  return undefined;
 }
 
 function getSignedIdentityKeysBlob(): SignedIdentityKeysBlob {
@@ -298,6 +304,167 @@
   };
 }
 
+const olmAPI: OlmAPI = {
+  async initializeCryptoAccount(): Promise<void> {
+    const sqliteQueryExecutor = getSQLiteQueryExecutor();
+    if (!sqliteQueryExecutor) {
+      throw new Error('Database not initialized');
+    }
+
+    const contentAccountResult = getOrCreateOlmAccount(
+      sqliteQueryExecutor.getContentAccountID(),
+    );
+    const notificationAccountResult = getOrCreateOlmAccount(
+      sqliteQueryExecutor.getNotifsAccountID(),
+    );
+    const contentSessions = getOlmSessions(contentAccountResult.picklingKey);
+
+    cryptoStore = {
+      contentAccountPickleKey: contentAccountResult.picklingKey,
+      contentAccount: contentAccountResult.account,
+      contentSessions,
+      notificationAccountPickleKey: notificationAccountResult.picklingKey,
+      notificationAccount: notificationAccountResult.account,
+    };
+
+    persistCryptoStore();
+  },
+  async encrypt(content: string, deviceID: string): Promise<string> {
+    if (!cryptoStore) {
+      throw new Error('Crypto account not initialized');
+    }
+    const session = cryptoStore.contentSessions[deviceID];
+    if (!session) {
+      throw new Error(`No session for deviceID: ${deviceID}`);
+    }
+    const { body } = session.encrypt(content);
+
+    persistCryptoStore();
+
+    return body;
+  },
+  async decrypt(encryptedContent: string, deviceID: string): Promise<string> {
+    if (!cryptoStore) {
+      throw new Error('Crypto account not initialized');
+    }
+
+    const session = cryptoStore.contentSessions[deviceID];
+    if (!session) {
+      throw new Error(`No session for deviceID: ${deviceID}`);
+    }
+
+    const result = session.decrypt(
+      olmEncryptedMessageTypes.TEXT,
+      encryptedContent,
+    );
+
+    persistCryptoStore();
+
+    return result;
+  },
+  async contentInboundSessionCreator(
+    contentIdentityKeys: OLMIdentityKeys,
+    initialEncryptedContent: string,
+  ): Promise<string> {
+    if (!cryptoStore) {
+      throw new Error('Crypto account not initialized');
+    }
+    const { contentAccount, contentSessions } = cryptoStore;
+
+    const session = new olm.Session();
+    session.create_inbound_from(
+      contentAccount,
+      contentIdentityKeys.curve25519,
+      initialEncryptedContent,
+    );
+
+    contentAccount.remove_one_time_keys(session);
+    const initialEncryptedMessage = session.decrypt(
+      olmEncryptedMessageTypes.PREKEY,
+      initialEncryptedContent,
+    );
+
+    contentSessions[contentIdentityKeys.ed25519] = session;
+    persistCryptoStore();
+
+    return initialEncryptedMessage;
+  },
+  async getOneTimeKeys(numberOfKeys: number): Promise<OneTimeKeysResultValues> {
+    if (!cryptoStore) {
+      throw new Error('Crypto account not initialized');
+    }
+    const { contentAccount, notificationAccount } = cryptoStore;
+
+    const contentOneTimeKeys = getAccountOneTimeKeys(
+      contentAccount,
+      numberOfKeys,
+    );
+    contentAccount.mark_keys_as_published();
+
+    const notificationsOneTimeKeys = getAccountOneTimeKeys(
+      notificationAccount,
+      numberOfKeys,
+    );
+    notificationAccount.mark_keys_as_published();
+
+    persistCryptoStore();
+
+    return { contentOneTimeKeys, notificationsOneTimeKeys };
+  },
+  async validateAndUploadPrekeys(authMetadata): Promise<void> {
+    const { userID, deviceID, accessToken } = authMetadata;
+    if (!userID || !deviceID || !accessToken) {
+      return;
+    }
+    const identityClient = getIdentityClient();
+
+    if (!identityClient) {
+      throw new Error('Identity client not initialized');
+    }
+
+    if (!cryptoStore) {
+      throw new Error('Crypto account not initialized');
+    }
+    const { contentAccount, notificationAccount } = cryptoStore;
+
+    // Content and notification accounts' keys are always rotated at the same
+    // time so we only need to check one of them.
+    if (shouldRotatePrekey(contentAccount)) {
+      contentAccount.generate_prekey();
+      notificationAccount.generate_prekey();
+    }
+    if (shouldForgetPrekey(contentAccount)) {
+      contentAccount.forget_old_prekey();
+      notificationAccount.forget_old_prekey();
+    }
+    persistCryptoStore();
+
+    if (!contentAccount.unpublished_prekey()) {
+      return;
+    }
+
+    const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
+      getAccountPrekeysSet(notificationAccount);
+    const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
+      getAccountPrekeysSet(contentAccount);
+
+    if (!notifPrekeySignature || !contentPrekeySignature) {
+      throw new Error('Prekey signature is missing');
+    }
+
+    await identityClient.publishWebPrekeys({
+      contentPrekey,
+      contentPrekeySignature,
+      notifPrekey,
+      notifPrekeySignature,
+    });
+    contentAccount.mark_prekey_as_published();
+    notificationAccount.mark_prekey_as_published();
+
+    persistCryptoStore();
+  },
+};
+
 export {
   clearCryptoStore,
   processAppOlmApiRequest,
diff --git a/web/types/worker-types.js b/web/types/worker-types.js
--- a/web/types/worker-types.js
+++ b/web/types/worker-types.js
@@ -1,7 +1,7 @@
 // @flow
 
 import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
-import type { CryptoStore } from 'lib/types/crypto-types.js';
+import type { OlmAPI, CryptoStore } from 'lib/types/crypto-types.js';
 import type { PlatformDetails } from 'lib/types/device-types.js';
 import type {
   IdentityServiceClient,
@@ -29,6 +29,7 @@
   INITIALIZE_CRYPTO_ACCOUNT: 12,
   CREATE_IDENTITY_SERVICE_CLIENT: 13,
   CALL_IDENTITY_CLIENT_METHOD: 14,
+  CALL_OLM_API_METHOD: 15,
 });
 
 export const workerWriteRequests: $ReadOnlyArray<number> = [
@@ -42,6 +43,7 @@
 
 export const workerOlmAPIRequests: $ReadOnlyArray<number> = [
   workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT,
+  workerRequestMessageTypes.CALL_OLM_API_METHOD,
 ];
 
 export const workerIdentityClientRequests: $ReadOnlyArray<number> = [
@@ -131,6 +133,12 @@
   +args: $ReadOnlyArray<mixed>,
 };
 
+export type CallOLMApiMethodRequestMessage = {
+  +type: 15,
+  +method: $Keys<OlmAPI>,
+  +args: $ReadOnlyArray<mixed>,
+};
+
 export type WorkerRequestMessage =
   | PingWorkerRequestMessage
   | InitWorkerRequestMessage
@@ -146,7 +154,8 @@
   | BackupRestoreRequestMessage
   | InitializeCryptoAccountRequestMessage
   | CreateIdentityServiceClientRequestMessage
-  | CallIdentityClientMethodRequestMessage;
+  | CallIdentityClientMethodRequestMessage
+  | CallOLMApiMethodRequestMessage;
 
 export type WorkerRequestProxyMessage = {
   +id: number,
@@ -160,6 +169,7 @@
   GET_CURRENT_USER_ID: 2,
   GET_PERSIST_STORAGE_ITEM: 3,
   CALL_IDENTITY_CLIENT_METHOD: 4,
+  CALL_OLM_API_METHOD: 5,
 });
 
 export type PongWorkerResponseMessage = {
@@ -187,12 +197,18 @@
   +result: mixed,
 };
 
+export type CallOLMApiMethodResponseMessage = {
+  +type: 5,
+  +result: mixed,
+};
+
 export type WorkerResponseMessage =
   | PongWorkerResponseMessage
   | ClientStoreResponseMessage
   | GetCurrentUserIDResponseMessage
   | GetPersistStorageItemResponseMessage
-  | CallIdentityClientMethodResponseMessage;
+  | CallIdentityClientMethodResponseMessage
+  | CallOLMApiMethodResponseMessage;
 
 export type WorkerResponseProxyMessage = {
   +id?: number,