diff --git a/lib/tunnelbroker/use-peer-to-peer-message-handler.js b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
--- a/lib/tunnelbroker/use-peer-to-peer-message-handler.js
+++ b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
@@ -259,6 +259,7 @@
           const decrypted = await olmAPI.decryptAndPersist(
             message.encryptedData,
             message.senderInfo.deviceID,
+            message.senderInfo.userID,
             messageID,
           );
           console.log(
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
@@ -161,6 +161,7 @@
   +decryptAndPersist: (
     encryptedData: EncryptedData,
     deviceID: string,
+    userID: string,
     messageID: string,
   ) => Promise<string>,
   +contentInboundSessionCreator: (
diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js
--- a/lib/types/sqlite-types.js
+++ b/lib/types/sqlite-types.js
@@ -25,6 +25,7 @@
   +senderDeviceID: string,
   +plaintext: string,
   +status: string,
+  +senderUserID: string,
 };
 
 export type OutboundP2PMessage = {
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
@@ -47,7 +47,7 @@
     "olm_persist_sessions",
     "metadata",
     "outbound_p2p_messages",
-    "received_messages_to_device",
+    "inbound_p2p_messages",
     "integrity_store",
     "persist_storage",
     "keyservers",
@@ -789,6 +789,21 @@
   return create_table(db, query, "message_search");
 }
 
+bool recreate_inbound_p2p_messages_table(sqlite3 *db) {
+  std::string query =
+      "DROP TABLE IF EXISTS received_messages_to_device;"
+      "CREATE TABLE IF NOT EXISTS inbound_p2p_messages ("
+      "  id INTEGER PRIMARY KEY,"
+      "  message_id TEXT NOT NULL,"
+      "  sender_device_id TEXT NOT NULL,"
+      "  plaintext TEXT NOT NULL,"
+      "  status TEXT NOT NULL,"
+      "  sender_user_id TEXT NOT NULL"
+      ");";
+
+  return create_table(db, query, "inbound_p2p_messages");
+}
+
 bool create_schema(sqlite3 *db) {
   char *error;
   int sidebarSourceTypeInt = static_cast<int>(MessageType::SIDEBAR_SOURCE);
@@ -936,12 +951,13 @@
       "  thread_activity_store_entry TEXT NOT NULL"
       ");"
 
-      "CREATE TABLE IF NOT EXISTS received_messages_to_device ("
+      "CREATE TABLE IF NOT EXISTS inbound_p2p_messages ("
       "  id INTEGER PRIMARY KEY,"
       "  message_id TEXT NOT NULL,"
       "  sender_device_id TEXT NOT NULL,"
       "  plaintext TEXT NOT NULL,"
-      "  status TEXT NOT NULL"
+      "  status TEXT NOT NULL,"
+      "  sender_user_id TEXT NOT NULL"
       ");"
 
       "CREATE TABLE IF NOT EXISTS entries ("
@@ -1222,7 +1238,8 @@
      {48, {create_messages_idx_target_message_type_time, true}},
      {49, {add_supports_auto_retry_column_to_p2p_messages_table, true}},
      {50, {create_message_search_table, true}},
-     {51, {update_messages_idx_target_message_type_time, true}}}};
+     {51, {update_messages_idx_target_message_type_time, true}},
+     {52, {recreate_inbound_p2p_messages_table, true}}}};
 
 enum class MigrationResult { SUCCESS, FAILURE, NOT_APPLIED };
 
@@ -2624,9 +2641,9 @@
 void SQLiteQueryExecutor::addInboundP2PMessage(
     InboundP2PMessage message) const {
   static std::string addMessage =
-      "REPLACE INTO received_messages_to_device ("
-      " message_id, sender_device_id, plaintext, status)"
-      "VALUES (?, ?, ?, ?);";
+      "REPLACE INTO inbound_p2p_messages ("
+      "  message_id, sender_device_id, plaintext, status, sender_user_id)"
+      "VALUES (?, ?, ?, ?, ?);";
 
   replaceEntity<InboundP2PMessage>(
       SQLiteQueryExecutor::getConnection(), addMessage, message);
@@ -2635,8 +2652,8 @@
 std::vector<InboundP2PMessage>
 SQLiteQueryExecutor::getAllInboundP2PMessage() const {
   static std::string query =
-      "SELECT message_id, sender_device_id, plaintext, status "
-      "FROM received_messages_to_device;";
+      "SELECT message_id, sender_device_id, plaintext, status, sender_user_id "
+      "FROM inbound_p2p_messages;";
   return getAllEntities<InboundP2PMessage>(
       SQLiteQueryExecutor::getConnection(), query);
 }
@@ -2648,7 +2665,7 @@
   }
 
   std::stringstream removeMessagesSQLStream;
-  removeMessagesSQLStream << "DELETE FROM received_messages_to_device "
+  removeMessagesSQLStream << "DELETE FROM inbound_p2p_messages "
                              "WHERE message_id IN "
                           << getSQLStatementArray(ids.size()) << ";";
 
diff --git a/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h b/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h
--- a/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h
+++ b/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h
@@ -13,6 +13,7 @@
   std::string sender_device_id;
   std::string plaintext;
   std::string status;
+  std::string sender_user_id;
 
   static InboundP2PMessage fromSQLResult(sqlite3_stmt *sqlRow, int idx) {
     return InboundP2PMessage{
@@ -20,6 +21,7 @@
         getStringFromSQLRow(sqlRow, idx + 1),
         getStringFromSQLRow(sqlRow, idx + 2),
         getStringFromSQLRow(sqlRow, idx + 3),
+        getStringFromSQLRow(sqlRow, idx + 4),
     };
   }
 
@@ -27,7 +29,8 @@
     bindStringToSQL(message_id, sql, idx);
     bindStringToSQL(sender_device_id, sql, idx + 1);
     bindStringToSQL(plaintext, sql, idx + 2);
-    return bindStringToSQL(status, sql, idx + 3);
+    bindStringToSQL(status, sql, idx + 3);
+    return bindStringToSQL(sender_user_id, sql, idx + 4);
   }
 };
 
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
@@ -170,6 +170,7 @@
       jsi::Runtime &rt,
       jsi::Object encryptedDataJSI,
       jsi::String deviceID,
+      jsi::String userID,
       jsi::String messageID) override;
   virtual jsi::Value
   signMessage(jsi::Runtime &rt, jsi::String message) override;
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
@@ -1885,6 +1885,7 @@
     jsi::Runtime &rt,
     jsi::Object encryptedDataJSI,
     jsi::String deviceID,
+    jsi::String userID,
     jsi::String messageID) {
   size_t messageType =
       std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber());
@@ -1899,6 +1900,7 @@
 
   auto deviceIDCpp{deviceID.utf8(rt)};
   auto messageIDCpp{messageID.utf8(rt)};
+  auto userIDCpp{userID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
@@ -1927,7 +1929,8 @@
                         messageIDCpp,
                         deviceIDCpp,
                         decryptedMessage,
-                        "decrypted"};
+                        "decrypted",
+                        userIDCpp};
 
                     DatabaseManager::getQueryExecutor().beginTransaction();
                     DatabaseManager::getQueryExecutor().addInboundP2PMessage(
@@ -2668,6 +2671,8 @@
                       innerRt, "senderDeviceID", msg.sender_device_id);
                   jsiMsg.setProperty(innerRt, "plaintext", msg.plaintext);
                   jsiMsg.setProperty(innerRt, "status", msg.status);
+                  jsiMsg.setProperty(
+                      innerRt, "senderUserID", msg.sender_user_id);
                   jsiMessages.setValueAtIndex(innerRt, writeIdx++, jsiMsg);
                 }
 
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)->decrypt(rt, args[0].asObject(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptAndPersist(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
-  return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->decryptAndPersist(rt, args[0].asObject(rt), args[1].asString(rt), args[2].asString(rt));
+  return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->decryptAndPersist(rt, args[0].asObject(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->signMessage(rt, args[0].asString(rt));
@@ -269,7 +269,7 @@
   methodMap_["encryptNotification"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encryptNotification};
   methodMap_["encryptAndPersist"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encryptAndPersist};
   methodMap_["decrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt};
-  methodMap_["decryptAndPersist"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptAndPersist};
+  methodMap_["decryptAndPersist"] = MethodMetadata {4, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptAndPersist};
   methodMap_["signMessage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage};
   methodMap_["verifySignature"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_verifySignature};
   methodMap_["getCodeVersion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCodeVersion};
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 encryptNotification(jsi::Runtime &rt, jsi::String payload, jsi::String deviceID) = 0;
   virtual jsi::Value encryptAndPersist(jsi::Runtime &rt, jsi::String message, jsi::String deviceID, jsi::String messageID) = 0;
   virtual jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) = 0;
-  virtual jsi::Value decryptAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) = 0;
+  virtual jsi::Value decryptAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String userID, jsi::String messageID) = 0;
   virtual jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) = 0;
   virtual jsi::Value verifySignature(jsi::Runtime &rt, jsi::String publicKey, jsi::String message, jsi::String signature) = 0;
   virtual double getCodeVersion(jsi::Runtime &rt) = 0;
@@ -361,13 +361,13 @@
       return bridging::callFromJs<jsi::Value>(
           rt, &T::decrypt, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID));
     }
-    jsi::Value decryptAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) override {
+    jsi::Value decryptAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String userID, jsi::String messageID) override {
       static_assert(
-          bridging::getParameterCount(&T::decryptAndPersist) == 4,
-          "Expected decryptAndPersist(...) to have 4 parameters");
+          bridging::getParameterCount(&T::decryptAndPersist) == 5,
+          "Expected decryptAndPersist(...) to have 5 parameters");
 
       return bridging::callFromJs<jsi::Value>(
-          rt, &T::decryptAndPersist, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID), std::move(messageID));
+          rt, &T::decryptAndPersist, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID), std::move(userID), std::move(messageID));
     }
     jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) override {
       static_assert(
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -119,6 +119,7 @@
   +decryptAndPersist: (
     encryptedData: Object,
     deviceID: string,
+    userID: string,
     messageID: string,
   ) => Promise<string>;
   +signMessage: (message: string) => Promise<string>;
@@ -209,6 +210,7 @@
   +decryptAndPersist: (
     encryptedData: EncryptedData,
     deviceID: string,
+    userID: string,
     messageID: string,
   ) => Promise<string>;
   +initializeContentInboundSession: (
diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp
--- a/web/cpp/SQLiteQueryExecutorBindings.cpp
+++ b/web/cpp/SQLiteQueryExecutorBindings.cpp
@@ -134,6 +134,7 @@
   value_object<InboundP2PMessage>("InboundP2PMessage")
       .field("messageID", &InboundP2PMessage::message_id)
       .field("senderDeviceID", &InboundP2PMessage::sender_device_id)
+      .field("senderUserID", &InboundP2PMessage::sender_user_id)
       .field("plaintext", &InboundP2PMessage::plaintext)
       .field("status", &InboundP2PMessage::status);
 
diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001

literal 0
Hc$@<O00001

diff --git a/web/shared-worker/queries/inbound-p2p-message-queries.test.js b/web/shared-worker/queries/inbound-p2p-message-queries.test.js
--- a/web/shared-worker/queries/inbound-p2p-message-queries.test.js
+++ b/web/shared-worker/queries/inbound-p2p-message-queries.test.js
@@ -15,6 +15,7 @@
 const TEST_MSG_1: InboundP2PMessage = {
   messageID: 'id-1',
   senderDeviceID: device1,
+  senderUserID: 'user-1',
   plaintext: 'decrypted-1',
   status: 'none',
 };
@@ -22,6 +23,7 @@
 const TEST_MSG_2: InboundP2PMessage = {
   messageID: 'id-2',
   senderDeviceID: device2,
+  senderUserID: 'user-1',
   plaintext: 'decrypted-2',
   status: 'none',
 };
@@ -29,6 +31,7 @@
 const TEST_MSG_3: InboundP2PMessage = {
   messageID: 'id-3',
   senderDeviceID: device1,
+  senderUserID: 'user-1',
   plaintext: 'decrypted-3',
   status: 'none',
 };
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
@@ -654,6 +654,7 @@
   async decryptAndPersist(
     encryptedData: EncryptedData,
     deviceID: string,
+    userID: string,
     messageID: string,
   ): Promise<string> {
     if (!cryptoStore) {
@@ -688,6 +689,7 @@
     const receivedMessage: InboundP2PMessage = {
       messageID,
       senderDeviceID: deviceID,
+      senderUserID: userID,
       plaintext: result,
       status: 'decrypted',
     };