diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/keyserver/src/push/crypto.js
@@ -11,13 +11,13 @@
   PlainTextWebNotificationPayload,
   WebNotification,
   PlainTextWNSNotification,
-  PlainTextWNSNotificationPayload,
   WNSNotification,
   AndroidVisualNotification,
   AndroidVisualNotificationPayload,
   AndroidBadgeOnlyNotification,
   AndroidNotificationRescind,
   NotificationTargetDevice,
+  SenderDeviceDescriptor,
 } from 'lib/types/notif-types.js';
 import { toBase64URL } from 'lib/utils/base64.js';
 
@@ -27,6 +27,7 @@
 
 async function encryptAPNsNotification(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   notification: apn.Notification,
   codeVersion?: ?number,
   notificationSizeValidator?: apn.Notification => boolean,
@@ -58,8 +59,7 @@
   encryptedNotification.pushType = 'alert';
   encryptedNotification.mutableContent = true;
 
-  const { id, keyserverID, ...payloadSansUnencryptedData } =
-    notification.payload;
+  const { id, ...payloadSansUnencryptedData } = notification.payload;
   const unencryptedPayload = {
     ...payloadSansUnencryptedData,
     badge: notification.aps.badge.toString(),
@@ -95,6 +95,10 @@
     );
 
     encryptedNotification.payload.encryptedPayload = serializedPayload.body;
+    encryptedNotification.payload = {
+      ...senderDeviceID,
+      ...encryptedNotification.payload,
+    };
 
     if (codeVersion && codeVersion >= 254 && codeVersion % 2 === 0) {
       encryptedNotification.aps = {
@@ -137,10 +141,15 @@
 
 async function encryptAndroidNotificationPayload<T>(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   unencryptedPayload: T,
-  payloadSizeValidator?: (T | { +encryptedPayload: string }) => boolean,
+  payloadSizeValidator?: (
+    T | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+  ) => boolean,
 ): Promise<{
-  +resultPayload: T | { +encryptedPayload: string },
+  +resultPayload:
+    | T
+    | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
   +payloadSizeExceeded: boolean,
   +encryptionOrder?: number,
 }> {
@@ -161,7 +170,11 @@
         serializedPayload,
       }: {
         +[string]: EncryptResult,
-      }) => payloadSizeValidator({ encryptedPayload: serializedPayload.body });
+      }) =>
+        payloadSizeValidator({
+          encryptedPayload: serializedPayload.body,
+          ...senderDeviceID,
+        });
     }
 
     const {
@@ -177,7 +190,10 @@
       dbPersistCondition,
     );
     return {
-      resultPayload: { encryptedPayload: serializedPayload.body },
+      resultPayload: {
+        encryptedPayload: serializedPayload.body,
+        ...senderDeviceID,
+      },
       payloadSizeExceeded: !!dbPersistConditionViolated,
       encryptionOrder,
     };
@@ -197,6 +213,7 @@
 }
 
 async function encryptAndroidVisualNotification(
+  senderDeviceID: SenderDeviceDescriptor,
   cookieID: string,
   notification: AndroidVisualNotification,
   notificationSizeValidator?: AndroidVisualNotification => boolean,
@@ -206,11 +223,11 @@
   +payloadSizeExceeded: boolean,
   +encryptionOrder?: number,
 }> {
-  const { id, keyserverID, ...rest } = notification.data;
+  const { id, ...rest } = notification.data;
 
-  let unencryptedData = { keyserverID };
+  let unencryptedData = {};
   if (id) {
-    unencryptedData = { ...unencryptedData, id };
+    unencryptedData = { id };
   }
 
   let unencryptedPayload = rest;
@@ -221,7 +238,9 @@
   let payloadSizeValidator;
   if (notificationSizeValidator) {
     payloadSizeValidator = (
-      payload: AndroidVisualNotificationPayload | { +encryptedPayload: string },
+      payload:
+        | AndroidVisualNotificationPayload
+        | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
     ) => {
       return notificationSizeValidator({
         data: { ...unencryptedData, ...payload },
@@ -231,6 +250,7 @@
   const { resultPayload, payloadSizeExceeded, encryptionOrder } =
     await encryptAndroidNotificationPayload(
       cookieID,
+      senderDeviceID,
       unencryptedPayload,
       payloadSizeValidator,
     );
@@ -248,31 +268,32 @@
 
 async function encryptAndroidSilentNotification(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
 ): Promise<AndroidNotificationRescind | AndroidBadgeOnlyNotification> {
   // We don't validate payload size for rescind
   // since they are expected to be small and
   // never exceed any FCM limit
-  const { keyserverID, ...unencryptedPayload } = notification.data;
+  const { ...unencryptedPayload } = notification.data;
   const { resultPayload } = await encryptAndroidNotificationPayload(
     cookieID,
+    senderDeviceID,
     unencryptedPayload,
   );
   if (resultPayload.encryptedPayload) {
     return {
-      data: { keyserverID, ...resultPayload },
+      data: { ...resultPayload },
     };
   }
 
   if (resultPayload.rescind) {
     return {
-      data: { keyserverID, ...resultPayload },
+      data: { ...resultPayload },
     };
   }
 
   return {
     data: {
-      keyserverID,
       ...resultPayload,
     },
   };
@@ -280,9 +301,14 @@
 
 async function encryptBasicPayload<T>(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   basicPayload: T,
 ): Promise<
-  | { +encryptedPayload: string, +encryptionOrder?: number }
+  | $ReadOnly<{
+      ...SenderDeviceDescriptor,
+      +encryptedPayload: string,
+      +encryptionOrder?: number,
+    }>
   | { ...T, +encryptionFailed: '1' },
 > {
   const unencryptedSerializedPayload = JSON.stringify(basicPayload);
@@ -300,6 +326,7 @@
     });
 
     return {
+      ...senderDeviceID,
       encryptedPayload: serializedPayload.body,
       encryptionOrder,
     };
@@ -314,37 +341,42 @@
 
 async function encryptWebNotification(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   notification: PlainTextWebNotification,
 ): Promise<{ +notification: WebNotification, +encryptionOrder?: number }> {
-  const { id, keyserverID, ...payloadSansId } = notification;
+  const { id, ...payloadSansId } = notification;
   const { encryptionOrder, ...encryptionResult } =
     await encryptBasicPayload<PlainTextWebNotificationPayload>(
       cookieID,
+      senderDeviceID,
       payloadSansId,
     );
+
   return {
-    notification: { id, keyserverID, ...encryptionResult },
+    notification: { id, ...encryptionResult },
     encryptionOrder,
   };
 }
 
 async function encryptWNSNotification(
   cookieID: string,
+  senderDeviceID: SenderDeviceDescriptor,
   notification: PlainTextWNSNotification,
 ): Promise<{ +notification: WNSNotification, +encryptionOrder?: number }> {
-  const { keyserverID, ...payloadSansKeyserverID } = notification;
   const { encryptionOrder, ...encryptionResult } =
-    await encryptBasicPayload<PlainTextWNSNotificationPayload>(
+    await encryptBasicPayload<PlainTextWNSNotification>(
       cookieID,
-      payloadSansKeyserverID,
+      senderDeviceID,
+      notification,
     );
   return {
-    notification: { keyserverID, ...encryptionResult },
+    notification: { ...encryptionResult },
     encryptionOrder,
   };
 }
 
 function prepareEncryptedAPNsNotifications(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: apn.Notification,
   codeVersion?: ?number,
@@ -363,6 +395,7 @@
     async ({ cookieID, deviceToken, blobHolder }) => {
       const notif = await encryptAPNsNotification(
         cookieID,
+        senderDeviceID,
         notification,
         codeVersion,
         notificationSizeValidator,
@@ -375,6 +408,7 @@
 }
 
 function prepareEncryptedIOSNotificationRescind(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: apn.Notification,
   codeVersion?: ?number,
@@ -389,6 +423,7 @@
     async ({ deviceToken, cookieID }) => {
       const { notification: notif } = await encryptAPNsNotification(
         cookieID,
+        senderDeviceID,
         notification,
         codeVersion,
       );
@@ -399,6 +434,7 @@
 }
 
 function prepareEncryptedAndroidVisualNotifications(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: AndroidVisualNotification,
   notificationSizeValidator?: (
@@ -416,6 +452,7 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID, blobHolder }) => {
       const notif = await encryptAndroidVisualNotification(
+        senderDeviceID,
         cookieID,
         notification,
         notificationSizeValidator,
@@ -428,6 +465,7 @@
 }
 
 function prepareEncryptedAndroidSilentNotifications(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
 ): Promise<
@@ -442,6 +480,7 @@
     async ({ deviceToken, cookieID }) => {
       const notif = await encryptAndroidSilentNotification(
         cookieID,
+        senderDeviceID,
         notification,
       );
       return { deviceToken, cookieID, notification: notif };
@@ -451,6 +490,7 @@
 }
 
 function prepareEncryptedWebNotifications(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: PlainTextWebNotification,
 ): Promise<
@@ -462,7 +502,11 @@
 > {
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
-      const notif = await encryptWebNotification(cookieID, notification);
+      const notif = await encryptWebNotification(
+        cookieID,
+        senderDeviceID,
+        notification,
+      );
       return { ...notif, deviceToken };
     },
   );
@@ -470,6 +514,7 @@
 }
 
 function prepareEncryptedWNSNotifications(
+  senderDeviceID: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: PlainTextWNSNotification,
 ): Promise<
@@ -481,7 +526,11 @@
 > {
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
-      const notif = await encryptWNSNotification(cookieID, notification);
+      const notif = await encryptWNSNotification(
+        cookieID,
+        senderDeviceID,
+        notification,
+      );
       return { ...notif, deviceToken };
     },
   );
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
@@ -9,6 +9,7 @@
 import type {
   NotificationTargetDevice,
   TargetedAndroidNotification,
+  SenderDeviceDescriptor,
 } from 'lib/types/notif-types.js';
 import { threadSubscriptions } from 'lib/types/subscription-types.js';
 import { threadPermissions } from 'lib/types/thread-permission-types.js';
@@ -274,10 +275,12 @@
 }
 
 async function conditionallyEncryptNotification<T>(
+  senderDeviceID: SenderDeviceDescriptor,
   notification: T,
   codeVersion: ?number,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   encryptCallback: (
+    senderDeviceID: SenderDeviceDescriptor,
     devices: $ReadOnlyArray<NotificationTargetDevice>,
     notification: T,
     codeVersion?: ?number,
@@ -298,6 +301,7 @@
     }));
   }
   const notifications = await encryptCallback(
+    senderDeviceID,
     devices,
     notification,
     codeVersion,
@@ -350,6 +354,7 @@
           },
         };
   return await conditionallyEncryptNotification(
+    { keyserverID },
     notification,
     codeVersion,
     devices,
@@ -375,10 +380,10 @@
       rescindID: notifID,
       setUnreadStatus: 'true',
       threadID,
-      keyserverID,
     },
   };
   const targetedRescinds = await conditionallyEncryptNotification(
+    { keyserverID },
     notification,
     codeVersion,
     devices,
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
@@ -992,7 +992,6 @@
   notification.pushType = 'alert';
   notification.payload.id = uniqueID;
   notification.payload.threadID = threadID;
-  notification.payload.keyserverID = keyserverID;
 
   if (platformDetails.codeVersion && platformDetails.codeVersion > 198) {
     notification.mutableContent = true;
@@ -1033,6 +1032,7 @@
   if (platformDetails.platform === 'macos') {
     const macOSNotifsWithoutMessageInfos =
       await prepareEncryptedAPNsNotifications(
+        { keyserverID },
         devices,
         notification,
         platformDetails.codeVersion,
@@ -1046,6 +1046,7 @@
   }
 
   const notifsWithMessageInfos = await prepareEncryptedAPNsNotifications(
+    { keyserverID },
     devices,
     copyWithMessageInfos,
     platformDetails.codeVersion,
@@ -1054,7 +1055,10 @@
 
   const devicesWithExcessiveSizeNoHolders = notifsWithMessageInfos
     .filter(({ payloadSizeExceeded }) => payloadSizeExceeded)
-    .map(({ deviceToken, cookieID }) => ({ deviceToken, cookieID }));
+    .map(({ deviceToken, cookieID }) => ({
+      deviceToken,
+      cookieID,
+    }));
 
   if (devicesWithExcessiveSizeNoHolders.length === 0) {
     return notifsWithMessageInfos.map(
@@ -1112,6 +1116,7 @@
   }
 
   const notifsWithoutMessageInfos = await prepareEncryptedAPNsNotifications(
+    { keyserverID },
     devicesWithExcessiveSize,
     notification,
     platformDetails.codeVersion,
@@ -1202,7 +1207,6 @@
   const { merged, ...rest } = notifTexts;
   const notification = {
     data: {
-      keyserverID,
       badge: unreadCount.toString(),
       ...rest,
       threadID,
@@ -1259,6 +1263,7 @@
 
   const notifsWithMessageInfos =
     await prepareEncryptedAndroidVisualNotifications(
+      { keyserverID },
       devices,
       copyWithMessageInfos,
       notificationsSizeValidator,
@@ -1320,6 +1325,7 @@
 
   const notifsWithoutMessageInfos =
     await prepareEncryptedAndroidVisualNotifications(
+      { keyserverID },
       devicesWithExcessiveSize,
       notification,
     );
@@ -1379,7 +1385,6 @@
     unreadCount,
     id,
     threadID,
-    keyserverID,
   };
 
   const shouldBeEncrypted = hasMinCodeVersion(convertedData.platformDetails, {
@@ -1390,7 +1395,11 @@
     return devices.map(({ deviceToken }) => ({ deviceToken, notification }));
   }
 
-  return prepareEncryptedWebNotifications(devices, notification);
+  return prepareEncryptedWebNotifications(
+    { keyserverID },
+    devices,
+    notification,
+  );
 }
 
 type WNSNotifInputData = {
@@ -1422,7 +1431,6 @@
     ...rest,
     unreadCount,
     threadID,
-    keyserverID,
   };
 
   if (
@@ -1442,7 +1450,11 @@
       notification,
     }));
   }
-  return await prepareEncryptedWNSNotifications(devices, notification);
+  return await prepareEncryptedWNSNotifications(
+    { keyserverID },
+    devices,
+    notification,
+  );
 }
 
 type NotificationInfo =
@@ -1791,11 +1803,11 @@
       });
       notification.badge = unreadCount;
       notification.pushType = 'alert';
-      notification.payload.keyserverID = keyserverID;
       const preparePromise: Promise<PreparePushResult[]> = (async () => {
         let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
         if (codeVersion > 222) {
           const notificationsArray = await prepareEncryptedAPNsNotifications(
+            { keyserverID },
             deviceInfos,
             notification,
             codeVersion,
@@ -1839,7 +1851,7 @@
         badgeOnly: '1',
       };
       const notification = {
-        data: { ...notificationData, keyserverID },
+        data: { ...notificationData },
       };
       const preparePromise: Promise<PreparePushResult[]> = (async () => {
         let targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>;
@@ -1847,6 +1859,7 @@
         if (codeVersion > 222) {
           const notificationsArray =
             await prepareEncryptedAndroidSilentNotifications(
+              { keyserverID },
               deviceInfos,
               notification,
             );
@@ -1904,6 +1917,7 @@
         let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
         if (shouldBeEncrypted) {
           const notificationsArray = await prepareEncryptedAPNsNotifications(
+            { keyserverID },
             deviceInfos,
             notification,
             codeVersion,
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -26,6 +26,10 @@
     prefix: t.maybe(t.String),
   });
 
+export type SenderDeviceDescriptor =
+  | { +keyserverID: string }
+  | { +senderDeviceID: string };
+
 export type PlainTextWebNotificationPayload = {
   +body: string,
   +prefix?: string,
@@ -35,23 +39,23 @@
   +encryptionFailed?: '1',
 };
 
-export type PlainTextWebNotification = {
+export type PlainTextWebNotification = $ReadOnly<{
   +id: string,
-  +keyserverID: string,
   ...PlainTextWebNotificationPayload,
-};
+}>;
 
-export type EncryptedWebNotification = {
+export type EncryptedWebNotification = $ReadOnly<{
+  ...SenderDeviceDescriptor,
   +id: string,
-  +keyserverID: string,
   +encryptedPayload: string,
-};
+  +type?: '0' | '1',
+}>;
 
 export type WebNotification =
   | PlainTextWebNotification
   | EncryptedWebNotification;
 
-export type PlainTextWNSNotificationPayload = {
+export type PlainTextWNSNotification = {
   +body: string,
   +prefix?: string,
   +title: string,
@@ -60,15 +64,11 @@
   +encryptionFailed?: '1',
 };
 
-export type PlainTextWNSNotification = {
-  +keyserverID: string,
-  ...PlainTextWNSNotificationPayload,
-};
-
-export type EncryptedWNSNotification = {
-  +keyserverID: string,
+export type EncryptedWNSNotification = $ReadOnly<{
+  ...SenderDeviceDescriptor,
   +encryptedPayload: string,
-};
+  +type?: '0' | '1',
+}>;
 
 export type WNSNotification =
   | PlainTextWNSNotification
@@ -85,39 +85,46 @@
   +encryptionFailed?: '1',
 }>;
 
-export type AndroidVisualNotificationPayload = $ReadOnly<
-  | {
-      ...AndroidVisualNotificationPayloadBase,
-      +messageInfos?: string,
-    }
-  | {
-      ...AndroidVisualNotificationPayloadBase,
-      +blobHash: string,
-      +encryptionKey: string,
-    },
->;
+type AndroidSmallVisualNotificationPayload = $ReadOnly<{
+  ...AndroidVisualNotificationPayloadBase,
+  +messageInfos?: string,
+}>;
+
+type AndroidLargeVisualNotificationPayload = $ReadOnly<{
+  ...AndroidVisualNotificationPayloadBase,
+  +blobHash: string,
+  +encryptionKey: string,
+}>;
+
+export type AndroidVisualNotificationPayload =
+  | AndroidSmallVisualNotificationPayload
+  | AndroidLargeVisualNotificationPayload;
+
+type EncryptedThinThreadPayload = {
+  +keyserverID: string,
+  +encryptedPayload: string,
+  +type?: '0' | '1',
+};
+
+type EncryptedThickThreadPayload = {
+  +senderDeviceID: string,
+  +encryptedPayload: string,
+  +type?: '0' | '1',
+};
 
 export type AndroidVisualNotification = {
   +data: $ReadOnly<{
     +id?: string,
-    +keyserverID: string,
     ...
-      | {
-          ...AndroidVisualNotificationPayloadBase,
-          +messageInfos?: string,
-        }
-      | {
-          ...AndroidVisualNotificationPayloadBase,
-          +blobHash: string,
-          +encryptionKey: string,
-        }
-      | { +encryptedPayload: string },
+      | AndroidSmallVisualNotificationPayload
+      | AndroidLargeVisualNotificationPayload
+      | EncryptedThinThreadPayload
+      | EncryptedThickThreadPayload,
   }>,
 };
 
 export type AndroidNotificationRescind = {
   +data: $ReadOnly<{
-    +keyserverID: string,
     ...
       | {
           +badge: string,
@@ -127,20 +134,21 @@
           +threadID: string,
           +encryptionFailed?: string,
         }
-      | { +encryptedPayload: string },
+      | EncryptedThinThreadPayload
+      | EncryptedThickThreadPayload,
   }>,
 };
 
 export type AndroidBadgeOnlyNotification = {
   +data: $ReadOnly<{
-    +keyserverID: string,
     ...
       | {
           +badge: string,
           +badgeOnly: '1',
           +encryptionFailed?: string,
         }
-      | { +encryptedPayload: string },
+      | EncryptedThinThreadPayload
+      | EncryptedThickThreadPayload,
   }>,
 };
 
diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js
--- a/web/push-notif/notif-crypto-utils.js
+++ b/web/push-notif/notif-crypto-utils.js
@@ -1,6 +1,7 @@
 // @flow
 
 import olm from '@commapp/olm';
+import invariant from 'invariant';
 import localforage from 'localforage';
 
 import {
@@ -64,6 +65,7 @@
   encryptedNotification: EncryptedWebNotification,
 ): Promise<PlainTextWebNotification | WebNotifDecryptionError> {
   const { id, keyserverID, encryptedPayload } = encryptedNotification;
+  invariant(keyserverID, 'KeyserverID must be present to decrypt a notif');
   const utilsData = await localforage.getItem<WebNotifsServiceUtilsData>(
     WEB_NOTIFS_SERVICE_UTILS_KEY,
   );
@@ -108,6 +110,8 @@
     );
 
     const { unreadCount } = decryptedNotification;
+
+    invariant(keyserverID, 'Keyserver ID must be set to update badge counts');
     await updateNotifsUnreadCountStorage({
       [keyserverID]: unreadCount,
     });