diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -368,7 +368,7 @@
   devices: $ReadOnlyArray<NotificationTargetDevice>,
 ): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
   threadID = await validateOutput(platformDetails, tID, threadID);
-  return await createAndroidNotificationRescind(
+  const { targetedNotifications } = await createAndroidNotificationRescind(
     encryptedNotifUtilsAPI,
     {
       senderDeviceDescriptor: { keyserverID },
@@ -379,6 +379,7 @@
     },
     devices,
   );
+  return targetedNotifications;
 }
 
 export { rescindPushNotifs };
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -1093,11 +1093,12 @@
     inputData,
   );
 
-  return createAndroidVisualNotification(
+  const { targetedNotifications } = await createAndroidVisualNotification(
     encryptedNotifUtilsAPI,
     convertedData,
     devices,
   );
+  return targetedNotifications;
 }
 
 async function prepareWebNotification(
@@ -1110,7 +1111,13 @@
     inputData,
   );
 
-  return createWebNotification(encryptedNotifUtilsAPI, convertedData, devices);
+  const { targetedNotifications } = await createWebNotification(
+    encryptedNotifUtilsAPI,
+    convertedData,
+    devices,
+  );
+
+  return targetedNotifications;
 }
 
 async function prepareWNSNotification(
@@ -1122,7 +1129,12 @@
     wnsNotifInputDataValidator,
     inputData,
   );
-  return createWNSNotification(encryptedNotifUtilsAPI, convertedData, devices);
+  const { targetedNotifications } = await createWNSNotification(
+    encryptedNotifUtilsAPI,
+    convertedData,
+    devices,
+  );
+  return targetedNotifications;
 }
 
 type NotificationInfo =
@@ -1518,7 +1530,7 @@
     for (const [versionKey, deviceInfos] of androidVersionsToTokens) {
       const { codeVersion, stateVersion } = stringToVersionKey(versionKey);
       const preparePromise: Promise<PreparePushResult[]> = (async () => {
-        const targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification> =
+        const { targetedNotifications } =
           await createAndroidBadgeOnlyNotification(
             encryptedNotifUtilsAPI,
             {
diff --git a/lib/push/android-notif-creators.js b/lib/push/android-notif-creators.js
--- a/lib/push/android-notif-creators.js
+++ b/lib/push/android-notif-creators.js
@@ -6,6 +6,8 @@
 import {
   prepareEncryptedAndroidVisualNotifications,
   prepareEncryptedAndroidSilentNotifications,
+  prepareLargeNotifData,
+  type LargeNotifData,
 } from './crypto.js';
 import { hasMinCodeVersion } from '../shared/version-utils.js';
 import type { PlatformDetails } from '../types/device-types.js';
@@ -67,7 +69,13 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: AndroidNotifInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
+  largeNotifToEncryptionResultPromises?: {
+    [string]: Promise<LargeNotifData>,
+  },
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>,
+  +largeNotifData?: LargeNotifData,
+}> {
   const {
     senderDeviceDescriptor,
     notifTexts,
@@ -146,11 +154,13 @@
         ? copyWithMessageInfos
         : notification;
 
-    return devices.map(({ deliveryID }) => ({
-      priority,
-      notification: notificationToSend,
-      deliveryID,
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        priority,
+        notification: notificationToSend,
+        deliveryID,
+      })),
+    };
   }
 
   const notificationsSizeValidator = (notif: AndroidVisualNotification) => {
@@ -176,25 +186,61 @@
     .map(({ cryptoID, deliveryID }) => ({ cryptoID, deliveryID }));
 
   if (devicesWithExcessiveSizeNoHolders.length === 0) {
-    return notifsWithMessageInfos.map(
-      ({ notification: notif, deliveryID, encryptionOrder }) => ({
-        priority,
-        notification: notif,
-        deliveryID,
-        encryptionOrder,
-      }),
-    );
+    return {
+      targetedNotifications: notifsWithMessageInfos.map(
+        ({ notification: notif, deliveryID, encryptionOrder }) => ({
+          priority,
+          notification: notif,
+          deliveryID,
+          encryptionOrder,
+        }),
+      ),
+    };
   }
 
   const canQueryBlobService = hasMinCodeVersion(platformDetails, {
     native: 331,
   });
 
-  let blobHash, blobHolders, encryptionKey, blobUploadError;
-  if (canQueryBlobService) {
+  let blobHash,
+    blobHolders,
+    encryptionKey,
+    blobUploadError,
+    encryptedCopyWithMessageInfos;
+  const copyWithMessageInfosDataBlob = JSON.stringify(
+    copyWithMessageInfos.data,
+  );
+  if (
+    canQueryBlobService &&
+    largeNotifToEncryptionResultPromises &&
+    largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob]
+  ) {
+    const largeNotifData =
+      await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob];
+    blobHash = largeNotifData.blobHash;
+    encryptionKey = largeNotifData.encryptionKey;
+    blobHolders = largeNotifData.blobHolders;
+    encryptedCopyWithMessageInfos =
+      largeNotifData.encryptedCopyWithMessageInfos;
+  } else if (canQueryBlobService && largeNotifToEncryptionResultPromises) {
+    largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob] =
+      prepareLargeNotifData(
+        copyWithMessageInfosDataBlob,
+        devicesWithExcessiveSizeNoHolders.length,
+        encryptedNotifUtilsAPI,
+      );
+
+    const largeNotifData =
+      await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob];
+    blobHash = largeNotifData.blobHash;
+    encryptionKey = largeNotifData.encryptionKey;
+    blobHolders = largeNotifData.blobHolders;
+    encryptedCopyWithMessageInfos =
+      largeNotifData.encryptedCopyWithMessageInfos;
+  } else if (canQueryBlobService) {
     ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
       await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
-        JSON.stringify(copyWithMessageInfos.data),
+        copyWithMessageInfosDataBlob,
         devicesWithExcessiveSizeNoHolders.length,
       ));
   }
@@ -251,10 +297,29 @@
     }),
   );
 
-  return [
+  const targetedNotifications = [
     ...targetedNotifsWithMessageInfos,
     ...targetedNotifsWithoutMessageInfos,
   ];
+
+  if (
+    !encryptedCopyWithMessageInfos ||
+    !blobHash ||
+    !blobHolders ||
+    !encryptionKey
+  ) {
+    return { targetedNotifications };
+  }
+
+  return {
+    targetedNotifications,
+    largeNotifData: {
+      blobHash,
+      blobHolders,
+      encryptionKey,
+      encryptedCopyWithMessageInfos,
+    },
+  };
 }
 type AndroidNotificationRescindInputData = {
   +senderDeviceDescriptor: SenderDeviceDescriptor,
@@ -268,7 +333,9 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: AndroidNotificationRescindInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>,
+}> {
   const {
     senderDeviceDescriptor,
     platformDetails,
@@ -298,11 +365,13 @@
 
   const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 233 });
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({
-      notification,
-      deliveryID,
-      priority: 'normal',
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        notification,
+        deliveryID,
+        priority: 'normal',
+      })),
+    };
   }
 
   const notifications = await prepareEncryptedAndroidSilentNotifications(
@@ -312,11 +381,15 @@
     notification,
   );
 
-  return notifications.map(({ deliveryID, notification: notif }) => ({
-    deliveryID,
-    notification: notif,
-    priority: 'normal',
-  }));
+  return {
+    targetedNotifications: notifications.map(
+      ({ deliveryID, notification: notif }) => ({
+        deliveryID,
+        notification: notif,
+        priority: 'normal',
+      }),
+    ),
+  };
 }
 
 type SenderDescriptorWithPlatformDetails = {
@@ -336,7 +409,9 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: AndroidBadgeOnlyNotificationInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>,
+}> {
   const { senderDeviceDescriptor, platformDetails, badge, threadID } =
     inputData;
 
@@ -361,11 +436,13 @@
   const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 222 });
 
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({
-      notification,
-      deliveryID,
-      priority: 'normal',
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        notification,
+        deliveryID,
+        priority: 'normal',
+      })),
+    };
   }
 
   const notifications = await prepareEncryptedAndroidSilentNotifications(
@@ -375,14 +452,16 @@
     notification,
   );
 
-  return notifications.map(
-    ({ notification: notif, deliveryID, encryptionOrder }) => ({
-      priority: 'normal',
-      notification: notif,
-      deliveryID,
-      encryptionOrder,
-    }),
-  );
+  return {
+    targetedNotifications: notifications.map(
+      ({ notification: notif, deliveryID, encryptionOrder }) => ({
+        priority: 'normal',
+        notification: notif,
+        deliveryID,
+        encryptionOrder,
+      }),
+    ),
+  };
 }
 
 export {
diff --git a/lib/push/apns-notif-creators.js b/lib/push/apns-notif-creators.js
--- a/lib/push/apns-notif-creators.js
+++ b/lib/push/apns-notif-creators.js
@@ -10,6 +10,8 @@
 import {
   prepareEncryptedAPNsVisualNotifications,
   prepareEncryptedAPNsSilentNotifications,
+  prepareLargeNotifData,
+  type LargeNotifData,
 } from './crypto.js';
 import { getAPNsNotificationTopic } from '../shared/notif-utils.js';
 import { hasMinCodeVersion } from '../shared/version-utils.js';
@@ -42,7 +44,13 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: APNsNotifInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAPNsNotification>> {
+  largeNotifToEncryptionResultPromises?: {
+    [string]: Promise<LargeNotifData>,
+  },
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>,
+  +largeNotifData?: LargeNotifData,
+}> {
   const {
     senderDeviceDescriptor,
     notifTexts,
@@ -166,10 +174,13 @@
     const notificationToSend = notificationSizeValidator(copyWithMessageInfos)
       ? copyWithMessageInfos
       : notification;
-    return devices.map(({ deliveryID }) => ({
+    const targetedNotifications = devices.map(({ deliveryID }) => ({
       notification: notificationToSend,
       deliveryID,
     }));
+    return {
+      targetedNotifications,
+    };
   }
 
   // The `messageInfos` field in notification payload is
@@ -183,12 +194,13 @@
         notification,
         platformDetails.codeVersion,
       );
-    return macOSNotifsWithoutMessageInfos.map(
+    const targetedNotifications = macOSNotifsWithoutMessageInfos.map(
       ({ notification: notif, deliveryID }) => ({
         notification: notif,
         deliveryID,
       }),
     );
+    return { targetedNotifications };
   }
 
   const notifsWithMessageInfos = await prepareEncryptedAPNsVisualNotifications(
@@ -208,7 +220,7 @@
     }));
 
   if (devicesWithExcessiveSizeNoHolders.length === 0) {
-    return notifsWithMessageInfos.map(
+    const targetedNotifications = notifsWithMessageInfos.map(
       ({
         notification: notif,
         deliveryID,
@@ -221,17 +233,55 @@
         encryptionOrder,
       }),
     );
+
+    return {
+      targetedNotifications,
+    };
   }
 
   const canQueryBlobService = hasMinCodeVersion(platformDetails, {
     native: 331,
   });
 
-  let blobHash, blobHolders, encryptionKey, blobUploadError;
-  if (canQueryBlobService) {
+  let blobHash,
+    blobHolders,
+    encryptionKey,
+    blobUploadError,
+    encryptedCopyWithMessageInfos;
+
+  const copyWithMessageInfosBlob = serializeAPNsNotif(copyWithMessageInfos);
+
+  if (
+    canQueryBlobService &&
+    largeNotifToEncryptionResultPromises &&
+    largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob]
+  ) {
+    const largeNotifData =
+      await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob];
+    blobHash = largeNotifData.blobHash;
+    encryptionKey = largeNotifData.encryptionKey;
+    blobHolders = largeNotifData.blobHolders;
+    encryptedCopyWithMessageInfos =
+      largeNotifData.encryptedCopyWithMessageInfos;
+  } else if (canQueryBlobService && largeNotifToEncryptionResultPromises) {
+    largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob] =
+      prepareLargeNotifData(
+        copyWithMessageInfosBlob,
+        devicesWithExcessiveSizeNoHolders.length,
+        encryptedNotifUtilsAPI,
+      );
+
+    const largeNotifData =
+      await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob];
+    blobHash = largeNotifData.blobHash;
+    encryptionKey = largeNotifData.encryptionKey;
+    blobHolders = largeNotifData.blobHolders;
+    encryptedCopyWithMessageInfos =
+      largeNotifData.encryptedCopyWithMessageInfos;
+  } else if (canQueryBlobService) {
     ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
       await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
-        serializeAPNsNotif(copyWithMessageInfos),
+        copyWithMessageInfosBlob,
         devicesWithExcessiveSizeNoHolders.length,
       ));
   }
@@ -300,11 +350,27 @@
       encryptionOrder,
     }),
   );
-
-  return [
+  const targetedNotifications = [
     ...targetedNotifsWithMessageInfos,
     ...targetedNotifsWithoutMessageInfos,
   ];
+  if (
+    !encryptedCopyWithMessageInfos ||
+    !blobHolders ||
+    !blobHash ||
+    !encryptionKey
+  ) {
+    return { targetedNotifications };
+  }
+  return {
+    targetedNotifications,
+    largeNotifData: {
+      blobHash,
+      blobHolders,
+      encryptionKey,
+      encryptedCopyWithMessageInfos,
+    },
+  };
 }
 
 type APNsNotificationRescindInputData = {
@@ -319,7 +385,9 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: APNsNotificationRescindInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAPNsNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>,
+}> {
   const {
     badge,
     rescindID,
@@ -384,10 +452,12 @@
 
   const shouldBeEncrypted = hasMinCodeVersion(platformDetails, { native: 233 });
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({
-      notification,
-      deliveryID,
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        notification,
+        deliveryID,
+      })),
+    };
   }
 
   const notifications = await prepareEncryptedAPNsSilentNotifications(
@@ -398,10 +468,14 @@
     platformDetails.codeVersion,
   );
 
-  return notifications.map(({ deliveryID, notification: notif }) => ({
-    deliveryID,
-    notification: notif,
-  }));
+  return {
+    targetedNotifications: notifications.map(
+      ({ deliveryID, notification: notif }) => ({
+        deliveryID,
+        notification: notif,
+      }),
+    ),
+  };
 }
 
 type SenderDescriptorWithPlatformDetails = {
@@ -421,7 +495,10 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: APNsBadgeOnlyNotificationInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedAPNsNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>,
+  +largeNotifData?: LargeNotifData,
+}> {
   const { senderDeviceDescriptor, platformDetails, threadID, badge } =
     inputData;
 
@@ -467,10 +544,12 @@
   }
 
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({
-      deliveryID,
-      notification,
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        deliveryID,
+        notification,
+      })),
+    };
   }
 
   const notifications = await prepareEncryptedAPNsSilentNotifications(
@@ -481,10 +560,14 @@
     platformDetails.codeVersion,
   );
 
-  return notifications.map(({ deliveryID, notification: notif }) => ({
-    deliveryID,
-    notification: notif,
-  }));
+  return {
+    targetedNotifications: notifications.map(
+      ({ deliveryID, notification: notif }) => ({
+        deliveryID,
+        notification: notif,
+      }),
+    ),
+  };
 }
 
 export {
diff --git a/lib/push/crypto.js b/lib/push/crypto.js
--- a/lib/push/crypto.js
+++ b/lib/push/crypto.js
@@ -1,6 +1,7 @@
 // @flow
 
 import invariant from 'invariant';
+import uuid from 'uuid';
 
 import type {
   PlainTextWebNotification,
@@ -610,6 +611,37 @@
   return Promise.all(notificationPromises);
 }
 
+export type LargeNotifData = {
+  +blobHolders: $ReadOnlyArray<string>,
+  +blobHash: string,
+  +encryptionKey: string,
+  +encryptedCopyWithMessageInfos: Uint8Array,
+};
+
+async function prepareLargeNotifData(
+  copyWithMessageInfos: string,
+  numberOfDevices: number,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+): Promise<LargeNotifData> {
+  const encryptionKey = await encryptedNotifUtilsAPI.generateAESKey();
+
+  const blobHolders = Array.from({ length: numberOfDevices }, () => uuid.v4());
+  const encryptedCopyWithMessageInfos =
+    await encryptedNotifUtilsAPI.encryptWithAESKey(
+      encryptionKey,
+      copyWithMessageInfos,
+    );
+  const blobHash = await encryptedNotifUtilsAPI.getBlobHash(
+    encryptedCopyWithMessageInfos,
+  );
+  return {
+    blobHolders,
+    blobHash,
+    encryptedCopyWithMessageInfos,
+    encryptionKey,
+  };
+}
+
 export {
   prepareEncryptedAPNsVisualNotifications,
   prepareEncryptedAPNsSilentNotifications,
@@ -617,4 +649,5 @@
   prepareEncryptedAndroidSilentNotifications,
   prepareEncryptedWebNotifications,
   prepareEncryptedWNSNotifications,
+  prepareLargeNotifData,
 };
diff --git a/lib/push/send-hooks.react.js b/lib/push/send-hooks.react.js
--- a/lib/push/send-hooks.react.js
+++ b/lib/push/send-hooks.react.js
@@ -164,17 +164,22 @@
         return;
       }
 
-      let allPreparedPushNotifs = preparedPushNotifs;
+      let allPreparedPushNotifs: ?PerUserTargetedNotifications =
+        preparedPushNotifs;
+
       if (preparedOwnDevicesPushNotifs && senderUserID) {
         allPreparedPushNotifs = {
           ...allPreparedPushNotifs,
-          [senderUserID]: preparedOwnDevicesPushNotifs,
+          [senderUserID]: {
+            targetedNotifications: preparedOwnDevicesPushNotifs,
+          },
         };
       }
 
       const sendPromises = [];
       for (const userID in allPreparedPushNotifs) {
-        for (const notif of allPreparedPushNotifs[userID]) {
+        for (const notif of allPreparedPushNotifs[userID]
+          .targetedNotifications) {
           if (notif.targetedNotification.notification.encryptionFailed) {
             continue;
           }
diff --git a/lib/push/send-utils.js b/lib/push/send-utils.js
--- a/lib/push/send-utils.js
+++ b/lib/push/send-utils.js
@@ -13,6 +13,7 @@
   createAPNsBadgeOnlyNotification,
   createAPNsNotificationRescind,
 } from './apns-notif-creators.js';
+import type { LargeNotifData } from './crypto.js';
 import {
   stringToVersionKey,
   getDevicesByPlatform,
@@ -468,7 +469,13 @@
     encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
     input: NotifCreatorInput,
     devices: $ReadOnlyArray<NotificationTargetDevice>,
-  ) => Promise<$ReadOnlyArray<TargetedNotificationType>>,
+    largeNotifToEncryptionResultPromises?: {
+      [string]: Promise<LargeNotifData>,
+    },
+  ) => Promise<{
+    +targetedNotifications: $ReadOnlyArray<TargetedNotificationType>,
+    +largeNotifData?: LargeNotifData,
+  }>,
   +notifCreatorInputBase: NotifCreatorinputBase,
   +transformInputBase: (
     inputBase: NotifCreatorinputBase,
@@ -492,12 +499,16 @@
     TargetedNotificationType,
     NotifCreatorInput,
   >,
-): Promise<
-  $ReadOnlyArray<{
+  largeNotifToEncryptionResultPromises?: {
+    [string]: Promise<LargeNotifData>,
+  },
+): Promise<{
+  +targetedNotificationsWithPlatform: $ReadOnlyArray<{
     +platform: PlatformType,
     +targetedNotification: TargetedNotificationType,
   }>,
-> {
+  +largeNotifDataArray?: $ReadOnlyArray<LargeNotifData>,
+}> {
   const {
     encryptedNotifUtilsAPI,
     versionToDevices,
@@ -508,12 +519,13 @@
   } = input;
 
   const promises: Array<
-    Promise<
-      $ReadOnlyArray<{
+    Promise<{
+      +targetedNotificationsWithPlatform: $ReadOnlyArray<{
         +platform: PlatformType,
         +targetedNotification: TargetedNotificationType,
       }>,
-    >,
+      +largeNotifData?: LargeNotifData,
+    }>,
   > = [];
 
   for (const [versionKey, devices] of versionToDevices) {
@@ -534,17 +546,40 @@
 
     promises.push(
       (async () => {
-        return (
-          await notifCreatorCallback(encryptedNotifUtilsAPI, inputData, devices)
-        ).map(targetedNotification => ({
-          platform,
-          targetedNotification,
-        }));
+        const { targetedNotifications, largeNotifData } =
+          await notifCreatorCallback(
+            encryptedNotifUtilsAPI,
+            inputData,
+            devices,
+            largeNotifToEncryptionResultPromises,
+          );
+        const targetedNotificationsWithPlatform = targetedNotifications.map(
+          targetedNotification => ({
+            platform,
+            targetedNotification,
+          }),
+        );
+        return { targetedNotificationsWithPlatform, largeNotifData };
       })(),
     );
   }
 
-  return (await Promise.all(promises)).flat();
+  const results = await Promise.all(promises);
+  const targetedNotifsWithPlatform = results
+    .map(
+      ({ targetedNotificationsWithPlatform }) =>
+        targetedNotificationsWithPlatform,
+    )
+    .flat();
+
+  const largeNotifDataArray = results
+    .map(({ largeNotifData }) => largeNotifData)
+    .filter(Boolean);
+
+  return {
+    largeNotifDataArray,
+    targetedNotificationsWithPlatform: targetedNotifsWithPlatform,
+  };
 }
 
 type BuildNotifsForUserDevicesInputData = {
@@ -565,7 +600,13 @@
 
 async function buildNotifsForUserDevices(
   inputData: BuildNotifsForUserDevicesInputData,
-): Promise<?$ReadOnlyArray<TargetedNotificationWithPlatform>> {
+  largeNotifToEncryptionResultPromises: {
+    [string]: Promise<LargeNotifData>,
+  },
+): Promise<?{
+  +targetedNotificationsWithPlatform: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+  +largeNotifDataArray?: $ReadOnlyArray<LargeNotifData>,
+}> {
   const {
     encryptedNotifUtilsAPI,
     senderDeviceDescriptor,
@@ -598,62 +639,71 @@
   const [{ threadID }] = newRawMessageInfos;
 
   const promises: Array<
-    Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>>,
+    Promise<{
+      +targetedNotificationsWithPlatform: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+      +largeNotifDataArray?: $ReadOnlyArray<LargeNotifData>,
+    }>,
   > = [];
 
   const iosVersionToDevices = devicesByPlatform.get('ios');
   if (iosVersionToDevices) {
     promises.push(
-      buildNotifsForPlatform({
-        platform: 'ios',
-        encryptedNotifUtilsAPI,
-        notifCreatorCallback: createAPNsVisualNotification,
-        transformInputBase: (inputBase, platformDetails) => ({
-          ...inputBase,
-          newRawMessageInfos: shimUnsupportedRawMessageInfos(
-            newRawMessageInfos,
+      buildNotifsForPlatform(
+        {
+          platform: 'ios',
+          encryptedNotifUtilsAPI,
+          notifCreatorCallback: createAPNsVisualNotification,
+          transformInputBase: (inputBase, platformDetails) => ({
+            ...inputBase,
+            newRawMessageInfos: shimUnsupportedRawMessageInfos(
+              newRawMessageInfos,
+              platformDetails,
+            ),
             platformDetails,
-          ),
-          platformDetails,
-        }),
-        notifCreatorInputBase: {
-          senderDeviceDescriptor,
-          notifTexts,
-          threadID,
-          collapseKey: undefined,
-          badgeOnly,
-          uniqueID: uuidv4(),
+          }),
+          notifCreatorInputBase: {
+            senderDeviceDescriptor,
+            notifTexts,
+            threadID,
+            collapseKey: undefined,
+            badgeOnly,
+            uniqueID: uuidv4(),
+          },
+          versionToDevices: iosVersionToDevices,
         },
-        versionToDevices: iosVersionToDevices,
-      }),
+        largeNotifToEncryptionResultPromises,
+      ),
     );
   }
 
   const androidVersionToDevices = devicesByPlatform.get('android');
   if (androidVersionToDevices) {
     promises.push(
-      buildNotifsForPlatform({
-        platform: 'android',
-        encryptedNotifUtilsAPI,
-        notifCreatorCallback: createAndroidVisualNotification,
-        transformInputBase: (inputBase, platformDetails) => ({
-          ...inputBase,
-          newRawMessageInfos: shimUnsupportedRawMessageInfos(
-            newRawMessageInfos,
+      buildNotifsForPlatform(
+        {
+          platform: 'android',
+          encryptedNotifUtilsAPI,
+          notifCreatorCallback: createAndroidVisualNotification,
+          transformInputBase: (inputBase, platformDetails) => ({
+            ...inputBase,
+            newRawMessageInfos: shimUnsupportedRawMessageInfos(
+              newRawMessageInfos,
+              platformDetails,
+            ),
             platformDetails,
-          ),
-          platformDetails,
-        }),
-        notifCreatorInputBase: {
-          senderDeviceDescriptor,
-          notifTexts,
-          threadID,
-          collapseKey: undefined,
-          badgeOnly,
-          notifID: uuidv4(),
+          }),
+          notifCreatorInputBase: {
+            senderDeviceDescriptor,
+            notifTexts,
+            threadID,
+            collapseKey: undefined,
+            badgeOnly,
+            notifID: uuidv4(),
+          },
+          versionToDevices: androidVersionToDevices,
         },
-        versionToDevices: androidVersionToDevices,
-      }),
+        largeNotifToEncryptionResultPromises,
+      ),
     );
   }
 
@@ -728,7 +778,23 @@
     );
   }
 
-  return (await Promise.all(promises)).flat();
+  const results = await Promise.all(promises);
+  const targetedNotifsWithPlatform = results
+    .map(
+      ({ targetedNotificationsWithPlatform }) =>
+        targetedNotificationsWithPlatform,
+    )
+    .flat();
+
+  const largeNotifDataArray = results
+    .map(({ largeNotifDataArray: array }) => array)
+    .filter(Boolean)
+    .flat();
+
+  return {
+    largeNotifDataArray,
+    targetedNotificationsWithPlatform: targetedNotifsWithPlatform,
+  };
 }
 
 async function buildRescindsForOwnDevices(
@@ -742,7 +808,10 @@
 ): Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>> {
   const { threadID } = rescindData;
   const promises: Array<
-    Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>>,
+    Promise<{
+      +targetedNotificationsWithPlatform: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+      ...
+    }>,
   > = [];
 
   const iosVersionToDevices = devicesByPlatform.get('ios');
@@ -784,7 +853,15 @@
       }),
     );
   }
-  return (await Promise.all(promises)).flat();
+
+  const results = await Promise.all(promises);
+  const targetedNotifsWithPlatform = results
+    .map(
+      ({ targetedNotificationsWithPlatform }) =>
+        targetedNotificationsWithPlatform,
+    )
+    .flat();
+  return targetedNotifsWithPlatform;
 }
 
 async function buildBadgeUpdatesForOwnDevices(
@@ -798,7 +875,10 @@
 ): Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>> {
   const { threadID } = badgeUpdateData;
   const promises: Array<
-    Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>>,
+    Promise<{
+      +targetedNotificationsWithPlatform: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+      ...
+    }>,
   > = [];
 
   const iosVersionToDevices = devicesByPlatform.get('ios');
@@ -860,11 +940,22 @@
       }),
     );
   }
-  return (await Promise.all(promises)).flat();
+
+  const results = await Promise.all(promises);
+  const targetedNotifsWithPlatform = results
+    .map(
+      ({ targetedNotificationsWithPlatform }) =>
+        targetedNotificationsWithPlatform,
+    )
+    .flat();
+  return targetedNotifsWithPlatform;
 }
 
 export type PerUserTargetedNotifications = {
-  +[userID: string]: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+  +[userID: string]: {
+    +targetedNotifications: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+    +largeNotifDataArray?: $ReadOnlyArray<LargeNotifData>,
+  },
 };
 
 type BuildNotifsFromPushInfoInputData = {
@@ -906,7 +997,10 @@
   }
 
   const perUserBuildNotifsResultPromises: {
-    [userID: string]: Promise<$ReadOnlyArray<TargetedNotificationWithPlatform>>,
+    [userID: string]: Promise<{
+      +targetedNotifications: $ReadOnlyArray<TargetedNotificationWithPlatform>,
+      +largeNotifDataArray?: $ReadOnlyArray<LargeNotifData>,
+    }>,
   } = {};
 
   const { usersToCollapsableNotifInfo, usersToCollapseKeysToInfo } =
@@ -916,6 +1010,10 @@
     usersToCollapsableNotifInfo,
   );
 
+  const largeNotifToEncryptionResultPromises: {
+    [string]: Promise<LargeNotifData>,
+  } = {};
+
   for (const userID in mergedUsersToCollapsableInfo) {
     const threadInfos = Object.fromEntries(
       [...threadIDs].map(threadID => [
@@ -935,26 +1033,45 @@
         // We always pass one element array here
         // because coalescing is not supported for
         // notifications generated on the client
-        buildNotifsForUserDevices({
-          encryptedNotifUtilsAPI,
-          senderDeviceDescriptor,
-          rawMessageInfos: notifInfo.newMessageInfos,
-          userID,
-          threadInfos,
-          subscriptions: pushInfo[userID].subscriptions,
-          userInfos,
-          getENSNames,
-          getFCNames,
-          devicesByPlatform,
-        }),
+        buildNotifsForUserDevices(
+          {
+            encryptedNotifUtilsAPI,
+            senderDeviceDescriptor,
+            rawMessageInfos: notifInfo.newMessageInfos,
+            userID,
+            threadInfos,
+            subscriptions: pushInfo[userID].subscriptions,
+            userInfos,
+            getENSNames,
+            getFCNames,
+            devicesByPlatform,
+          },
+          largeNotifToEncryptionResultPromises,
+        ),
       );
     }
 
     perUserBuildNotifsResultPromises[userID] = (async () => {
-      const singleNotificationResults = await Promise.all(
-        singleNotificationPromises,
-      );
-      return singleNotificationResults.filter(Boolean).flat();
+      const singleNotificationResults = (
+        await Promise.all(singleNotificationPromises)
+      ).filter(Boolean);
+
+      const targetedNotifsWithPlatform = singleNotificationResults
+        .map(
+          ({ targetedNotificationsWithPlatform }) =>
+            targetedNotificationsWithPlatform,
+        )
+        .flat();
+
+      const largeNotifDataArray = singleNotificationResults
+        .map(({ largeNotifDataArray: array }) => array)
+        .filter(Boolean)
+        .flat();
+      console.log(largeNotifDataArray);
+      return {
+        targetedNotifications: targetedNotifsWithPlatform,
+        largeNotifDataArray,
+      };
     })();
   }
 
diff --git a/lib/push/web-notif-creators.js b/lib/push/web-notif-creators.js
--- a/lib/push/web-notif-creators.js
+++ b/lib/push/web-notif-creators.js
@@ -39,7 +39,9 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: WebNotifInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedWebNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedWebNotification>,
+}> {
   const { id, notifTexts, threadID, unreadCount, senderDeviceDescriptor } =
     inputData;
 
@@ -56,15 +58,20 @@
   });
 
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({ deliveryID, notification }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        deliveryID,
+        notification,
+      })),
+    };
   }
-
-  return prepareEncryptedWebNotifications(
+  const targetedNotifications = await prepareEncryptedWebNotifications(
     encryptedNotifUtilsAPI,
     senderDeviceDescriptor,
     devices,
     notification,
   );
+  return { targetedNotifications };
 }
 
 export { createWebNotification };
diff --git a/lib/push/wns-notif-creators.js b/lib/push/wns-notif-creators.js
--- a/lib/push/wns-notif-creators.js
+++ b/lib/push/wns-notif-creators.js
@@ -39,7 +39,9 @@
   encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   inputData: WNSNotifInputData,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
-): Promise<$ReadOnlyArray<TargetedWNSNotification>> {
+): Promise<{
+  +targetedNotifications: $ReadOnlyArray<TargetedWNSNotification>,
+}> {
   const { notifTexts, threadID, unreadCount, senderDeviceDescriptor } =
     inputData;
   const { merged, ...rest } = notifTexts;
@@ -61,17 +63,20 @@
   });
 
   if (!shouldBeEncrypted) {
-    return devices.map(({ deliveryID }) => ({
-      deliveryID,
-      notification,
-    }));
+    return {
+      targetedNotifications: devices.map(({ deliveryID }) => ({
+        deliveryID,
+        notification,
+      })),
+    };
   }
-  return await prepareEncryptedWNSNotifications(
+  const targetedNotifications = await prepareEncryptedWNSNotifications(
     encryptedNotifUtilsAPI,
     senderDeviceDescriptor,
     devices,
     notification,
   );
+  return { targetedNotifications };
 }
 
 export { createWNSNotification };