diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj
--- a/native/ios/Comm.xcodeproj/project.pbxproj
+++ b/native/ios/Comm.xcodeproj/project.pbxproj
@@ -97,8 +97,8 @@
 		CBB0DF612B768007008E22FF /* CommMMKV.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBB0DF5F2B768007008E22FF /* CommMMKV.mm */; };
 		CBCA09062A8E0E7400F75B3E /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */; };
 		CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */; };
-		CBCF984F2BA499DA00DBC3D9 /* CommIOSBlobClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBCF984D2BA499DA00DBC3D9 /* CommIOSBlobClient.mm */; };
-		CBCF98502BA49A0500DBC3D9 /* CommIOSBlobClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBCF984D2BA499DA00DBC3D9 /* CommIOSBlobClient.mm */; };
+		CBCF984F2BA499DA00DBC3D9 /* CommIOSServicesClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBCF984D2BA499DA00DBC3D9 /* CommIOSServicesClient.mm */; };
+		CBCF98502BA49A0500DBC3D9 /* CommIOSServicesClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBCF984D2BA499DA00DBC3D9 /* CommIOSServicesClient.mm */; };
 		CBDEC69B28ED867000C17588 /* GlobalDBSingleton.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */; };
 		CBFBEEBA2B4ED90600729F1D /* RustBackupExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBFBEEB82B4ED90600729F1D /* RustBackupExecutor.cpp */; };
 		CBFE58292885852B003B94C9 /* ThreadOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBFE58282885852B003B94C9 /* ThreadOperations.cpp */; };
@@ -332,8 +332,8 @@
 		CBCA09042A8E0E6B00F75B3E /* StaffUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StaffUtils.h; sourceTree = "<group>"; };
 		CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StaffUtils.cpp; sourceTree = "<group>"; };
 		CBCF57AB2B05096F00EC4BC0 /* AESCryptoModuleObjCCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESCryptoModuleObjCCompat.h; path = Comm/CommAESCryptoUtils/AESCryptoModuleObjCCompat.h; sourceTree = "<group>"; };
-		CBCF984D2BA499DA00DBC3D9 /* CommIOSBlobClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CommIOSBlobClient.mm; path = Comm/CommIOSServices/CommIOSBlobClient.mm; sourceTree = "<group>"; };
-		CBCF984E2BA499DA00DBC3D9 /* CommIOSBlobClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSBlobClient.h; path = Comm/CommIOSServices/CommIOSBlobClient.h; sourceTree = "<group>"; };
+		CBCF984D2BA499DA00DBC3D9 /* CommIOSServicesClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CommIOSServicesClient.mm; path = Comm/CommIOSServices/CommIOSServicesClient.mm; sourceTree = "<group>"; };
+		CBCF984E2BA499DA00DBC3D9 /* CommIOSServicesClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSServicesClient.h; path = Comm/CommIOSServices/CommIOSServicesClient.h; sourceTree = "<group>"; };
 		CBDEC69928ED859600C17588 /* GlobalDBSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDBSingleton.h; sourceTree = "<group>"; };
 		CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GlobalDBSingleton.mm; path = Comm/GlobalDBSingleton.mm; sourceTree = "<group>"; };
 		CBF9DAE22B595934000EE771 /* EntityQueryHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EntityQueryHelpers.h; sourceTree = "<group>"; };
@@ -816,8 +816,8 @@
 		CBCF984C2BA499C200DBC3D9 /* CommIOSServices */ = {
 			isa = PBXGroup;
 			children = (
-				CBCF984E2BA499DA00DBC3D9 /* CommIOSBlobClient.h */,
-				CBCF984D2BA499DA00DBC3D9 /* CommIOSBlobClient.mm */,
+				CBCF984E2BA499DA00DBC3D9 /* CommIOSServicesClient.h */,
+				CBCF984D2BA499DA00DBC3D9 /* CommIOSServicesClient.mm */,
 			);
 			name = CommIOSServices;
 			sourceTree = "<group>";
@@ -1201,7 +1201,7 @@
 				8EF775682A74032C0046A385 /* CommRustModule.cpp in Sources */,
 				34055C152BAD31AC0008E713 /* SyncedMetadataStore.cpp in Sources */,
 				8E43C32C291E5B4A009378F5 /* TerminateApp.mm in Sources */,
-				CBCF984F2BA499DA00DBC3D9 /* CommIOSBlobClient.mm in Sources */,
+				CBCF984F2BA499DA00DBC3D9 /* CommIOSServicesClient.mm in Sources */,
 				B3B02EBF2B8538980020D118 /* CommunityStore.cpp in Sources */,
 				8BC9568529FC49B00060AE4A /* JSIRust.cpp in Sources */,
 				8EA59BD92A73DAB000EB4F53 /* rustJSI-generated.cpp in Sources */,
@@ -1262,7 +1262,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CBCF98502BA49A0500DBC3D9 /* CommIOSBlobClient.mm in Sources */,
+				CBCF98502BA49A0500DBC3D9 /* CommIOSServicesClient.mm in Sources */,
 				CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */,
 				CB3C0A3B2A125C8F009BD4DA /* NotificationsCryptoModule.cpp in Sources */,
 				CB90951F29534B32002F2A7F /* CommSecureStore.mm in Sources */,
diff --git a/native/ios/Comm/AppDelegate.mm b/native/ios/Comm/AppDelegate.mm
--- a/native/ios/Comm/AppDelegate.mm
+++ b/native/ios/Comm/AppDelegate.mm
@@ -40,7 +40,7 @@
 
 #import "CommConstants.h"
 #import "CommCoreModule.h"
-#import "CommIOSBlobClient.h"
+#import "CommIOSServicesClient.h"
 #import "CommMMKV.h"
 #import "CommRustModule.h"
 #import "CommUtilsModule.h"
@@ -415,12 +415,12 @@
 - (void)scheduleNSEBlobsDeletion {
   dispatch_async(
       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
-        [CommIOSBlobClient.sharedInstance deleteStoredBlobs];
+        [CommIOSServicesClient.sharedInstance deleteStoredBlobs];
       });
 }
 
 - (void)applicationWillResignActive:(UIApplication *)application {
-  [[CommIOSBlobClient sharedInstance] cancelOngoingRequests];
+  [[CommIOSServicesClient sharedInstance] cancelOngoingRequests];
 }
 
 // Copied from
diff --git a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.h b/native/ios/Comm/CommIOSServices/CommIOSServicesClient.h
rename from native/ios/Comm/CommIOSServices/CommIOSBlobClient.h
rename to native/ios/Comm/CommIOSServices/CommIOSServicesClient.h
--- a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.h
+++ b/native/ios/Comm/CommIOSServices/CommIOSServicesClient.h
@@ -1,8 +1,10 @@
 #import <Foundation/Foundation.h>
 
-@interface CommIOSBlobClient : NSObject
+@interface CommIOSServicesClient : NSObject
 + (id)sharedInstance;
 - (NSData *)getBlobSync:(NSString *)blobHash orSetError:(NSError **)error;
+- (NSDictionary *)getNotifsIdentityKeysFor:(NSString *)deviceID
+                                orSetError:(NSError **)error;
 - (void)deleteBlobAsyncWithHash:(NSString *)blobHash
                       andHolder:(NSString *)blobHolder
              withSuccessHandler:(void (^)())successHandler
diff --git a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm b/native/ios/Comm/CommIOSServices/CommIOSServicesClient.mm
rename from native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm
rename to native/ios/Comm/CommIOSServices/CommIOSServicesClient.mm
--- a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm
+++ b/native/ios/Comm/CommIOSServices/CommIOSServicesClient.mm
@@ -1,4 +1,4 @@
-#import "CommIOSBlobClient.h"
+#import "CommIOSServicesClient.h"
 #import "CommMMKV.h"
 #import "CommSecureStore.h"
 #import "Logger.h"
@@ -6,46 +6,50 @@
 #ifdef DEBUG
 NSString const *blobServiceAddress =
     @"https://blob.staging.commtechnologies.org";
+NSString const *identityServiceAddress =
+    @"https://identity.staging.commtechnologies.org:51004";
 #else
 NSString const *blobServiceAddress = @"https://blob.commtechnologies.org";
+NSString const *identityServiceAddress =
+    @"https://identity.commtechnologies.org:51004";
 #endif
 
-int const blobServiceQueryTimeLimit = 15;
+int const servicesQueryTimeLimit = 15;
 const std::string mmkvBlobHolderPrefix = "BLOB_HOLDER.";
 // The blob service expects slightly different keys in
 // delete reuqest payload than we use in notif payload
 NSString *const blobServiceHashKey = @"blob_hash";
 NSString *const blobServiceHolderKey = @"holder";
 
-@interface CommIOSBlobClient ()
-@property(nonatomic, strong) NSURLSession *sharedBlobServiceSession;
+@interface CommIOSServicesClient ()
+@property(nonatomic, strong) NSURLSession *sharedServicesSession;
 @end
 
-@implementation CommIOSBlobClient
+@implementation CommIOSServicesClient
 
 + (id)sharedInstance {
-  static CommIOSBlobClient *sharedBlobServiceClient = nil;
+  static CommIOSServicesClient *sharedServicesClient = nil;
   static dispatch_once_t onceToken;
 
   dispatch_once(&onceToken, ^{
     NSURLSessionConfiguration *config =
         [NSURLSessionConfiguration ephemeralSessionConfiguration];
 
-    [config setTimeoutIntervalForRequest:blobServiceQueryTimeLimit];
+    [config setTimeoutIntervalForRequest:servicesQueryTimeLimit];
     NSURLSession *session =
         [NSURLSession sessionWithConfiguration:config
                                       delegate:nil
                                  delegateQueue:[NSOperationQueue mainQueue]];
-    sharedBlobServiceClient = [[self alloc] init];
-    sharedBlobServiceClient.sharedBlobServiceSession = session;
+    sharedServicesClient = [[self alloc] init];
+    sharedServicesClient.sharedServicesSession = session;
   });
-  return sharedBlobServiceClient;
+  return sharedServicesClient;
 }
 
 - (NSData *)getBlobSync:(NSString *)blobHash orSetError:(NSError **)error {
   NSError *authTokenError = nil;
   NSString *authToken =
-      [CommIOSBlobClient _getAuthTokenOrSetError:&authTokenError];
+      [CommIOSServicesClient _getAuthTokenOrSetError:&authTokenError];
 
   if (authTokenError) {
     *error = authTokenError;
@@ -71,7 +75,7 @@
   __block NSData *blobContent = nil;
 
   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
-  NSURLSessionDataTask *task = [self.sharedBlobServiceSession
+  NSURLSessionDataTask *task = [self.sharedServicesSession
       dataTaskWithRequest:blobRequest
         completionHandler:^(
             NSData *_Nullable data,
@@ -109,8 +113,7 @@
   dispatch_semaphore_wait(
       semaphore,
       dispatch_time(
-          DISPATCH_TIME_NOW,
-          (int64_t)(blobServiceQueryTimeLimit * NSEC_PER_SEC)));
+          DISPATCH_TIME_NOW, (int64_t)(servicesQueryTimeLimit * NSEC_PER_SEC)));
   if (requestError) {
     *error = requestError;
     return nil;
@@ -118,13 +121,156 @@
   return blobContent;
 }
 
+- (NSDictionary *)getNotifsIdentityKeysFor:(NSString *)deviceID
+                                orSetError:(NSError *__autoreleasing *)error {
+  NSError *authTokenError = nil;
+  NSString *authToken =
+      [CommIOSServicesClient _getAuthTokenOrSetError:&authTokenError];
+
+  if (authTokenError) {
+    *error = authTokenError;
+    return nil;
+  }
+
+  NSString *base64URLEncodedDeviceID =
+      [[deviceID stringByReplacingOccurrencesOfString:@"+" withString:@"-"]
+          stringByReplacingOccurrencesOfString:@"/"
+                                    withString:@"_"];
+
+  NSString *urlString =
+      [NSString stringWithFormat:@"%@/device_inbound_keys?device_id=%@",
+                                 identityServiceAddress,
+                                 base64URLEncodedDeviceID];
+  NSURL *url = [NSURL URLWithString:urlString];
+
+  NSMutableURLRequest *identityRequest =
+      [[NSMutableURLRequest alloc] initWithURL:url];
+  [identityRequest setHTTPMethod:@"GET"];
+
+  // This is slightly against Apple docs:
+  // https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc#1776617
+  // but apparently there is no other way to
+  // do this and even Apple staff members
+  // advice to set this field manually to
+  // achieve token based authentication:
+  // https://developer.apple.com/forums/thread/89811
+  [identityRequest setValue:authToken forHTTPHeaderField:@"Authorization"];
+
+  __block NSError *requestError = nil;
+  __block NSDictionary *notifIdentityKeys = nil;
+
+  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+  NSURLSessionDataTask *task = [self.sharedServicesSession
+      dataTaskWithRequest:identityRequest
+        completionHandler:^(
+            NSData *_Nullable data,
+            NSURLResponse *_Nullable response,
+            NSError *_Nullable error) {
+          @try {
+            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+            if (httpResponse.statusCode > 299) {
+              NSString *errorMessage =
+                  [@"Fetching notifs identity key failed with the following "
+                   @"reason: "
+                      stringByAppendingString:[NSHTTPURLResponse
+                                                  localizedStringForStatusCode:
+                                                      httpResponse.statusCode]];
+              requestError = [NSError
+                  errorWithDomain:@"app.comm"
+                             code:httpResponse.statusCode
+                         userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
+              return;
+            }
+            if (error) {
+              requestError = error;
+              return;
+            }
+
+            NSError *jsonError;
+            NSDictionary *responseObject =
+                [NSJSONSerialization JSONObjectWithData:data
+                                                options:0
+                                                  error:&jsonError];
+
+            if (jsonError) {
+              requestError = jsonError;
+              return;
+            }
+
+            if (!responseObject[@"identityKeyInfo"] ||
+                !responseObject[@"identityKeyInfo"][@"keyPayload"]) {
+              NSString *errorMessage =
+                  [@"identityKeyInfo or keyPayload missing in identity service "
+                   @"response"
+                      stringByAppendingString:[NSHTTPURLResponse
+                                                  localizedStringForStatusCode:
+                                                      httpResponse.statusCode]];
+              requestError = [NSError
+                  errorWithDomain:@"app.comm"
+                             code:httpResponse.statusCode
+                         userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
+              return;
+            }
+
+            NSData *keyPayload =
+                [responseObject[@"identityKeyInfo"][@"keyPayload"]
+                    dataUsingEncoding:NSUTF8StringEncoding];
+            NSDictionary *identityKeys =
+                [NSJSONSerialization JSONObjectWithData:keyPayload
+                                                options:0
+                                                  error:&jsonError];
+
+            if (jsonError) {
+              requestError = jsonError;
+              return;
+            }
+
+            if (!identityKeys[@"notificationIdentityPublicKeys"]) {
+              NSString *errorMessage =
+                  [@"notificationIdentityPublicKeys missing in identity "
+                   @"service response"
+                      stringByAppendingString:[NSHTTPURLResponse
+                                                  localizedStringForStatusCode:
+                                                      httpResponse.statusCode]];
+              requestError = [NSError
+                  errorWithDomain:@"app.comm"
+                             code:httpResponse.statusCode
+                         userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
+              return;
+            }
+
+            notifIdentityKeys = identityKeys[@"notificationIdentityPublicKeys"];
+
+          } @catch (NSException *exception) {
+            comm::Logger::log(
+                "Received exception when fetching notifs identity key. "
+                "Details: " +
+                std::string([exception.reason UTF8String]));
+          } @finally {
+            dispatch_semaphore_signal(semaphore);
+          }
+        }];
+
+  [task resume];
+  dispatch_semaphore_wait(
+      semaphore,
+      dispatch_time(
+          DISPATCH_TIME_NOW, (int64_t)(servicesQueryTimeLimit * NSEC_PER_SEC)));
+  if (requestError) {
+    *error = requestError;
+    return nil;
+  }
+
+  return notifIdentityKeys;
+}
+
 - (void)deleteBlobAsyncWithHash:(NSString *)blobHash
                       andHolder:(NSString *)blobHolder
              withSuccessHandler:(void (^)())successHandler
               andFailureHandler:(void (^)(NSError *))failureHandler {
   NSError *authTokenError = nil;
   NSString *authToken =
-      [CommIOSBlobClient _getAuthTokenOrSetError:&authTokenError];
+      [CommIOSServicesClient _getAuthTokenOrSetError:&authTokenError];
 
   if (authTokenError) {
     comm::Logger::log(
@@ -151,7 +297,7 @@
                                                            options:0
                                                              error:nil];
 
-  NSURLSessionDataTask *task = [self.sharedBlobServiceSession
+  NSURLSessionDataTask *task = [self.sharedServicesSession
       dataTaskWithRequest:deleteRequest
         completionHandler:^(
             NSData *_Nullable data,
@@ -232,7 +378,7 @@
 }
 
 - (void)cancelOngoingRequests {
-  [self.sharedBlobServiceSession
+  [self.sharedServicesSession
       getAllTasksWithCompletionHandler:^(
           NSArray<__kindof NSURLSessionTask *> *_Nonnull tasks) {
         for (NSURLSessionTask *task in tasks) {
diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm
--- a/native/ios/NotificationService/NotificationService.mm
+++ b/native/ios/NotificationService/NotificationService.mm
@@ -1,6 +1,6 @@
 #import "NotificationService.h"
 #import "AESCryptoModuleObjCCompat.h"
-#import "CommIOSBlobClient.h"
+#import "CommIOSServicesClient.h"
 #import "CommMMKV.h"
 #import "Logger.h"
 #import "NotificationsCryptoModule.h"
@@ -550,8 +550,8 @@
 
   __block NSError *fetchError = nil;
   NSData *largePayloadBinary =
-      [CommIOSBlobClient.sharedInstance getBlobSync:blobHash
-                                         orSetError:&fetchError];
+      [CommIOSServicesClient.sharedInstance getBlobSync:blobHash
+                                             orSetError:&fetchError];
 
   if (fetchError) {
     comm::Logger::log(
@@ -564,7 +564,7 @@
       [NotificationService aesDecryptAndParse:largePayloadBinary
                                       withKey:encryptionKey];
   [self persistMessagePayload:largePayload];
-  [CommIOSBlobClient.sharedInstance
+  [CommIOSServicesClient.sharedInstance
       storeBlobForDeletionWithHash:blobHash
                          andHolder:content.userInfo[blobHolderKey]];
 }