diff --git a/native/cpp/CommonCpp/CryptoTools/CryptoModule.h b/native/cpp/CommonCpp/CryptoTools/CryptoModule.h
--- a/native/cpp/CommonCpp/CryptoTools/CryptoModule.h
+++ b/native/cpp/CommonCpp/CryptoTools/CryptoModule.h
@@ -58,8 +58,9 @@
       const std::string &targetDeviceId,
       const OlmBuffer &encryptedMessage,
       const OlmBuffer &idKeys,
+      int sessionVersion,
       const bool overwrite = true);
-  void initializeOutboundForSendingSession(
+  int initializeOutboundForSendingSession(
       const std::string &targetDeviceId,
       const OlmBuffer &idKeys,
       const OlmBuffer &preKeys,
diff --git a/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp b/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp
--- a/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp
+++ b/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp
@@ -261,8 +261,17 @@
     const std::string &targetDeviceId,
     const OlmBuffer &encryptedMessage,
     const OlmBuffer &idKeys,
+    int sessionVersion,
     const bool overwrite) {
   if (this->hasSessionFor(targetDeviceId)) {
+    std::shared_ptr<Session> existingSession =
+        getSessionByDeviceId(targetDeviceId);
+    if (existingSession->getVersion() > sessionVersion) {
+      throw std::runtime_error{"OLM_SESSION_ALREADY_CREATED"};
+    } else if (existingSession->getVersion() == sessionVersion) {
+      throw std::runtime_error{"OLM_SESSION_CREATION_RACE_CONDITION"};
+    }
+
     if (overwrite) {
       this->sessions.erase(this->sessions.find(targetDeviceId));
     } else {
@@ -276,16 +285,21 @@
       this->keys.identityKeys.data(),
       encryptedMessage,
       idKeys);
+  newSession->setVersion(sessionVersion);
   this->sessions.insert(make_pair(targetDeviceId, std::move(newSession)));
 }
 
-void CryptoModule::initializeOutboundForSendingSession(
+int CryptoModule::initializeOutboundForSendingSession(
     const std::string &targetDeviceId,
     const OlmBuffer &idKeys,
     const OlmBuffer &preKeys,
     const OlmBuffer &preKeySignature,
     const OlmBuffer &oneTimeKey) {
+  int newSessionVersion = 1;
   if (this->hasSessionFor(targetDeviceId)) {
+    std::shared_ptr<Session> existingSession =
+        getSessionByDeviceId(targetDeviceId);
+    newSessionVersion = existingSession->getVersion() + 1;
     Logger::log(
         "olm session overwritten for the device with id: " + targetDeviceId);
     this->sessions.erase(this->sessions.find(targetDeviceId));
@@ -297,7 +311,9 @@
       preKeys,
       preKeySignature,
       oneTimeKey);
+  newSession->setVersion(newSessionVersion);
   this->sessions.insert(make_pair(targetDeviceId, std::move(newSession)));
+  return newSessionVersion;
 }
 
 bool CryptoModule::hasSessionFor(const std::string &targetDeviceId) {
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
@@ -132,7 +132,8 @@
       jsi::Runtime &rt,
       jsi::String identityKeys,
       jsi::Object encryptedDataJSI,
-      jsi::String deviceID) override;
+      jsi::String deviceID,
+      double sessionVersion) override;
   virtual jsi::Value
   encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override;
   virtual jsi::Value decrypt(
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
@@ -1163,16 +1163,18 @@
         taskType job = [=, &innerRt]() {
           std::string error;
           crypto::EncryptedData initialEncryptedData;
+          int sessionVersion;
           try {
-            this->contentCryptoModule->initializeOutboundForSendingSession(
-                deviceIDCpp,
-                std::vector<uint8_t>(
-                    identityKeysCpp.begin(), identityKeysCpp.end()),
-                std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
-                std::vector<uint8_t>(
-                    prekeySignatureCpp.begin(), prekeySignatureCpp.end()),
-                std::vector<uint8_t>(
-                    oneTimeKeyCpp.begin(), oneTimeKeyCpp.end()));
+            sessionVersion =
+                this->contentCryptoModule->initializeOutboundForSendingSession(
+                    deviceIDCpp,
+                    std::vector<uint8_t>(
+                        identityKeysCpp.begin(), identityKeysCpp.end()),
+                    std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
+                    std::vector<uint8_t>(
+                        prekeySignatureCpp.begin(), prekeySignatureCpp.end()),
+                    std::vector<uint8_t>(
+                        oneTimeKeyCpp.begin(), oneTimeKeyCpp.end()));
 
             const std::string initMessage = "{\"type\": \"init\"}";
             initialEncryptedData =
@@ -1198,7 +1200,13 @@
                 "messageType",
                 static_cast<int>(initialEncryptedData.messageType));
 
-            promise->resolve(std::move(initialEncryptedDataJSI));
+            auto outboundSessionCreationResultJSI = jsi::Object(innerRt);
+            outboundSessionCreationResultJSI.setProperty(
+                innerRt, "encryptedData", initialEncryptedDataJSI);
+            outboundSessionCreationResultJSI.setProperty(
+                innerRt, "sessionVersion", sessionVersion);
+
+            promise->resolve(std::move(outboundSessionCreationResultJSI));
           });
         };
         this->cryptoThread->scheduleTask(job);
@@ -1209,7 +1217,8 @@
     jsi::Runtime &rt,
     jsi::String identityKeys,
     jsi::Object encryptedDataJSI,
-    jsi::String deviceID) {
+    jsi::String deviceID,
+    double sessionVersion) {
   auto identityKeysCpp{identityKeys.utf8(rt)};
   size_t messageType =
       std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber());
@@ -1227,7 +1236,8 @@
                 std::vector<uint8_t>(
                     encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
                 std::vector<uint8_t>(
-                    identityKeysCpp.begin(), identityKeysCpp.end()));
+                    identityKeysCpp.begin(), identityKeysCpp.end()),
+                static_cast<int>(sessionVersion));
             crypto::EncryptedData encryptedData{
                 std::vector<uint8_t>(
                     encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
--- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
+++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
@@ -109,7 +109,7 @@
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeContentOutboundSession(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
-  return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeContentInboundSession(rt, args[0].asString(rt), args[1].asObject(rt), args[2].asString(rt));
+  return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeContentInboundSession(rt, args[0].asString(rt), args[1].asObject(rt), args[2].asString(rt), args[3].asNumber());
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->encrypt(rt, args[0].asString(rt), args[1].asString(rt));
@@ -221,7 +221,7 @@
   methodMap_["removeKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage};
   methodMap_["getKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage};
   methodMap_["initializeContentOutboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession};
-  methodMap_["initializeContentInboundSession"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession};
+  methodMap_["initializeContentInboundSession"] = MethodMetadata {4, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession};
   methodMap_["encrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt};
   methodMap_["decrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt};
   methodMap_["signMessage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage};
diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h
--- a/native/cpp/CommonCpp/_generated/commJSI.h
+++ b/native/cpp/CommonCpp/_generated/commJSI.h
@@ -51,7 +51,7 @@
   virtual jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) = 0;
   virtual jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) = 0;
   virtual jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) = 0;
-  virtual jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID) = 0;
+  virtual jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion) = 0;
   virtual jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) = 0;
   virtual jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) = 0;
   virtual jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) = 0;
@@ -345,13 +345,13 @@
       return bridging::callFromJs<jsi::Value>(
           rt, &T::initializeContentOutboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(prekey), std::move(prekeySignature), std::move(oneTimeKey), std::move(deviceID));
     }
-    jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID) override {
+    jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion) override {
       static_assert(
-          bridging::getParameterCount(&T::initializeContentInboundSession) == 4,
-          "Expected initializeContentInboundSession(...) to have 4 parameters");
+          bridging::getParameterCount(&T::initializeContentInboundSession) == 5,
+          "Expected initializeContentInboundSession(...) to have 5 parameters");
 
       return bridging::callFromJs<jsi::Value>(
-          rt, &T::initializeContentInboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(encryptedContent), std::move(deviceID));
+          rt, &T::initializeContentInboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(encryptedContent), std::move(deviceID), std::move(sessionVersion));
     }
     jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override {
       static_assert(
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
@@ -7,6 +7,7 @@
   type OlmAPI,
   type OLMIdentityKeys,
   type EncryptedData,
+  type OutboundSessionCreationResult,
 } from 'lib/types/crypto-types.js';
 import type { OlmSessionInitializationInfo } from 'lib/types/request-types.js';
 
@@ -19,10 +20,10 @@
   getUserPublicKey: commCoreModule.getUserPublicKey,
   encrypt: commCoreModule.encrypt,
   decrypt: commCoreModule.decrypt,
-  // $FlowFixMe
   async contentInboundSessionCreator(
     contentIdentityKeys: OLMIdentityKeys,
     initialEncryptedData: EncryptedData,
+    sessionVersion: number,
   ): Promise<string> {
     const identityKeys = JSON.stringify({
       curve25519: contentIdentityKeys.curve25519,
@@ -32,13 +33,13 @@
       identityKeys,
       initialEncryptedData,
       contentIdentityKeys.ed25519,
+      sessionVersion,
     );
   },
   async contentOutboundSessionCreator(
     contentIdentityKeys: OLMIdentityKeys,
     contentInitializationInfo: OlmSessionInitializationInfo,
-    // $FlowFixMe
-  ): Promise<EncryptedData> {
+  ): Promise<OutboundSessionCreationResult> {
     const { prekey, prekeySignature, oneTimeKey } = contentInitializationInfo;
     const identityKeys = JSON.stringify({
       curve25519: contentIdentityKeys.curve25519,
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -19,6 +19,7 @@
   SignedPrekeys,
   ClientPublicKeys,
   EncryptedData,
+  OutboundSessionCreationResult,
 } from 'lib/types/crypto-types.js';
 import type { ClientDBDraftStoreOperation } from 'lib/types/draft-types.js';
 import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
@@ -110,11 +111,12 @@
     prekeySignature: string,
     oneTimeKey: string,
     deviceID: string,
-  ) => Promise<EncryptedData>;
+  ) => Promise<OutboundSessionCreationResult>;
   +initializeContentInboundSession: (
     identityKeys: string,
     encryptedContent: Object,
     deviceID: string,
+    sessionVersion: number,
   ) => Promise<string>;
   +encrypt: (message: string, deviceID: string) => Promise<EncryptedData>;
   +decrypt: (encryptedData: Object, deviceID: string) => Promise<string>;
@@ -160,6 +162,7 @@
     identityKeys: string,
     encryptedContent: EncryptedData,
     deviceID: string,
+    sessionVersion: number,
   ) => Promise<string>;
 }