diff --git a/keyserver/addons/rust-node-addon/rust-binding-types.js b/keyserver/addons/rust-node-addon/rust-binding-types.js
--- a/keyserver/addons/rust-node-addon/rust-binding-types.js
+++ b/keyserver/addons/rust-node-addon/rust-binding-types.js
@@ -1,17 +1,10 @@
 // @flow
 
 import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js';
-import type { UserLoginResponse } from 'lib/types/identity-service-types.js';
-
-type InboundKeyInfoResponse = {
-  +payload: string,
-  +payloadSignature: string,
-  +socialProof?: ?string,
-  +contentPrekey: string,
-  +contentPrekeySignature: string,
-  +notifPrekey: string,
-  +notifPrekeySignature: string,
-};
+import type {
+  InboundKeyInfoResponse,
+  UserLoginResponse,
+} from 'lib/types/identity-service-types.js';
 
 type RustNativeBindingAPI = {
   +loginUser: (
diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js
--- a/lib/types/identity-service-types.js
+++ b/lib/types/identity-service-types.js
@@ -24,3 +24,15 @@
   +deviceID: string,
   +commServicesAccessToken: string,
 };
+
+// This type should not be altered without also updating
+// InboundKeyInfoResponse in native/native_rust_library/src/lib.rs
+export type InboundKeyInfoResponse = {
+  +payload: string,
+  +payloadSignature: string,
+  +socialProof?: ?string,
+  +contentPrekey: string,
+  +contentPrekeySignature: string,
+  +notifPrekey: string,
+  +notifPrekeySignature: string,
+};
diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.h b/native/cpp/CommonCpp/NativeModules/CommRustModule.h
--- a/native/cpp/CommonCpp/NativeModules/CommRustModule.h
+++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.h
@@ -66,6 +66,12 @@
       jsi::String authDeviceID,
       jsi::String authAccessToken,
       jsi::String userID) override;
+  virtual jsi::Value getInboundKeysForUser(
+      jsi::Runtime &rt,
+      jsi::String authUserID,
+      jsi::String authDeviceID,
+      jsi::String authAccessToken,
+      jsi::String userID) override;
   virtual jsi::Value versionSupported(jsi::Runtime &rt) override;
 
 public:
diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp
--- a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp
@@ -247,6 +247,30 @@
       });
 }
 
+jsi::Value CommRustModule::getInboundKeysForUser(
+    jsi::Runtime &rt,
+    jsi::String authUserID,
+    jsi::String authDeviceID,
+    jsi::String authAccessToken,
+    jsi::String userID) {
+  return createPromiseAsJSIValue(
+      rt, [&, this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
+        std::string error;
+        try {
+          auto currentID = RustPromiseManager::instance.addPromise(
+              promise, this->jsInvoker_, innerRt);
+          identityGetInboundKeysForUser(
+              jsiStringToRustString(authUserID, innerRt),
+              jsiStringToRustString(authDeviceID, innerRt),
+              jsiStringToRustString(authAccessToken, innerRt),
+              jsiStringToRustString(userID, innerRt),
+              currentID);
+        } catch (const std::exception &e) {
+          error = e.what();
+        };
+      });
+}
+
 jsi::Value CommRustModule::versionSupported(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
diff --git a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp
--- a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp
+++ b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp
@@ -33,6 +33,9 @@
 static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_getOutboundKeysForUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommRustModuleSchemaCxxSpecJSI *>(&turboModule)->getOutboundKeysForUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt));
 }
+static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_getInboundKeysForUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
+  return static_cast<CommRustModuleSchemaCxxSpecJSI *>(&turboModule)->getInboundKeysForUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt));
+}
 static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_versionSupported(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommRustModuleSchemaCxxSpecJSI *>(&turboModule)->versionSupported(rt);
 }
@@ -46,6 +49,7 @@
   methodMap_["updatePassword"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_updatePassword};
   methodMap_["deleteUser"] = MethodMetadata {3, __hostFunction_CommRustModuleSchemaCxxSpecJSI_deleteUser};
   methodMap_["getOutboundKeysForUser"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_getOutboundKeysForUser};
+  methodMap_["getInboundKeysForUser"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_getInboundKeysForUser};
   methodMap_["versionSupported"] = MethodMetadata {0, __hostFunction_CommRustModuleSchemaCxxSpecJSI_versionSupported};
 }
 
diff --git a/native/cpp/CommonCpp/_generated/rustJSI.h b/native/cpp/CommonCpp/_generated/rustJSI.h
--- a/native/cpp/CommonCpp/_generated/rustJSI.h
+++ b/native/cpp/CommonCpp/_generated/rustJSI.h
@@ -27,6 +27,7 @@
   virtual jsi::Value updatePassword(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) = 0;
   virtual jsi::Value deleteUser(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) = 0;
   virtual jsi::Value getOutboundKeysForUser(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String userID) = 0;
+  virtual jsi::Value getInboundKeysForUser(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String userID) = 0;
   virtual jsi::Value versionSupported(jsi::Runtime &rt) = 0;
 
 };
@@ -105,6 +106,14 @@
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getOutboundKeysForUser, jsInvoker_, instance_, std::move(authUserID), std::move(authDeviceID), std::move(authAccessToken), std::move(userID));
     }
+    jsi::Value getInboundKeysForUser(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String userID) override {
+      static_assert(
+          bridging::getParameterCount(&T::getInboundKeysForUser) == 5,
+          "Expected getInboundKeysForUser(...) to have 5 parameters");
+
+      return bridging::callFromJs<jsi::Value>(
+          rt, &T::getInboundKeysForUser, jsInvoker_, instance_, std::move(authUserID), std::move(authDeviceID), std::move(authAccessToken), std::move(userID));
+    }
     jsi::Value versionSupported(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::versionSupported) == 1,
diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs
--- a/native/native_rust_library/src/lib.rs
+++ b/native/native_rust_library/src/lib.rs
@@ -3,7 +3,8 @@
 use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error;
 use ffi::{bool_callback, string_callback, void_callback};
 use grpc_clients::identity::protos::authenticated::{
-  OutboundKeyInfo, OutboundKeysForUserRequest, UpdateUserPasswordFinishRequest,
+  InboundKeyInfo, InboundKeysForUserRequest, OutboundKeyInfo,
+  OutboundKeysForUserRequest, UpdateUserPasswordFinishRequest,
   UpdateUserPasswordStartRequest,
 };
 use grpc_clients::identity::protos::client::{
@@ -124,6 +125,15 @@
       promise_id: u32,
     );
 
+    #[cxx_name = "identityGetInboundKeysForUser"]
+    fn get_inbound_keys_for_user(
+      auth_user_id: String,
+      auth_device_id: String,
+      auth_access_token: String,
+      user_id: String,
+      promise_id: u32,
+    );
+
     #[cxx_name = "identityGenerateNonce"]
     fn generate_nonce(promise_id: u32);
 
@@ -777,6 +787,10 @@
   user_id: String,
 }
 
+struct GetInboundKeysRequestInfo {
+  user_id: String,
+}
+
 // This struct should not be altered without also updating
 // OutboundKeyInfoResponse in lib/types/identity-service-types.js
 #[derive(Serialize)]
@@ -793,6 +807,20 @@
   pub one_time_notif_prekey: Option<String>,
 }
 
+// This struct should not be altered without also updating
+// InboundKeyInfoResponse in lib/types/identity-service-types.js
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct InboundKeyInfoResponse {
+  pub payload: String,
+  pub payload_signature: String,
+  pub social_proof: Option<String>,
+  pub content_prekey: String,
+  pub content_prekey_signature: String,
+  pub notif_prekey: String,
+  pub notif_prekey_signature: String,
+}
+
 impl TryFrom<OutboundKeyInfo> for OutboundKeyInfoResponse {
   type Error = Error;
 
@@ -891,6 +919,99 @@
   Ok(serde_json::to_string(&outbound_key_info)?)
 }
 
+impl TryFrom<InboundKeyInfo> for InboundKeyInfoResponse {
+  type Error = Error;
+
+  fn try_from(key_info: InboundKeyInfo) -> Result<Self, Error> {
+    let identity_info =
+      key_info.identity_info.ok_or(Error::MissingResponseData)?;
+
+    let IdentityKeyInfo {
+      payload,
+      payload_signature,
+      social_proof,
+    } = identity_info;
+
+    let content_prekey =
+      key_info.content_prekey.ok_or(Error::MissingResponseData)?;
+
+    let PreKey {
+      pre_key: content_prekey_value,
+      pre_key_signature: content_prekey_signature,
+    } = content_prekey;
+
+    let notif_prekey =
+      key_info.notif_prekey.ok_or(Error::MissingResponseData)?;
+
+    let PreKey {
+      pre_key: notif_prekey_value,
+      pre_key_signature: notif_prekey_signature,
+    } = notif_prekey;
+
+    Ok(Self {
+      payload,
+      payload_signature,
+      social_proof,
+      content_prekey: content_prekey_value,
+      content_prekey_signature,
+      notif_prekey: notif_prekey_value,
+      notif_prekey_signature,
+    })
+  }
+}
+
+fn get_inbound_keys_for_user(
+  auth_user_id: String,
+  auth_device_id: String,
+  auth_access_token: String,
+  user_id: String,
+  promise_id: u32,
+) {
+  RUNTIME.spawn(async move {
+    let get_inbound_keys_request_info = GetInboundKeysRequestInfo { user_id };
+    let auth_info = AuthInfo {
+      access_token: auth_access_token,
+      user_id: auth_user_id,
+      device_id: auth_device_id,
+    };
+    let result = get_inbound_keys_for_user_helper(
+      get_inbound_keys_request_info,
+      auth_info,
+    )
+    .await;
+    handle_string_result_as_callback(result, promise_id);
+  });
+}
+
+async fn get_inbound_keys_for_user_helper(
+  get_inbound_keys_request_info: GetInboundKeysRequestInfo,
+  auth_info: AuthInfo,
+) -> Result<String, Error> {
+  let mut identity_client = get_auth_client(
+    IDENTITY_SOCKET_ADDR,
+    auth_info.user_id,
+    auth_info.device_id,
+    auth_info.access_token,
+    CODE_VERSION,
+    DEVICE_TYPE.as_str_name().to_lowercase(),
+  )
+  .await?;
+  let response = identity_client
+    .get_inbound_keys_for_user(InboundKeysForUserRequest {
+      user_id: get_inbound_keys_request_info.user_id,
+    })
+    .await?
+    .into_inner();
+
+  let inbound_key_info: Vec<InboundKeyInfoResponse> = response
+    .devices
+    .into_values()
+    .map(InboundKeyInfoResponse::try_from)
+    .collect::<Result<Vec<_>, _>>()?;
+
+  Ok(serde_json::to_string(&inbound_key_info)?)
+}
+
 #[derive(
   Debug, derive_more::Display, derive_more::From, derive_more::Error,
 )]
diff --git a/native/schema/CommRustModuleSchema.js b/native/schema/CommRustModuleSchema.js
--- a/native/schema/CommRustModuleSchema.js
+++ b/native/schema/CommRustModuleSchema.js
@@ -61,6 +61,12 @@
     authAccessToken: string,
     userID: string,
   ) => Promise<string>;
+  +getInboundKeysForUser: (
+    authUserID: string,
+    authDeviceID: string,
+    authAccessToken: string,
+    userID: string,
+  ) => Promise<string>;
   +versionSupported: () => Promise<boolean>;
 }