diff --git a/keyserver/src/creators/message-creator.js b/keyserver/src/creators/message-creator.js
--- a/keyserver/src/creators/message-creator.js
+++ b/keyserver/src/creators/message-creator.js
@@ -446,7 +446,7 @@
       messageInfos,
       messageDatas,
       threadsToMessageIndices,
-      preUserPushInfo.notFocusedThreadIDs,
+      [...preUserPushInfo.notFocusedThreadIDs],
       userNotMemberOfSubthreads,
       (messageID: string) => fetchMessageInfoByID(viewer, messageID),
       userID,
@@ -457,7 +457,7 @@
       messageInfos,
       messageDatas,
       threadsToMessageIndices,
-      preUserPushInfo.notFocusedThreadIDs,
+      [...preUserPushInfo.notFocusedThreadIDs],
       userNotMemberOfSubthreads,
       (messageID: string) => fetchMessageInfoByID(viewer, messageID),
       userID,
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
@@ -20,7 +20,11 @@
 } from 'lib/push/android-notif-creators.js';
 import { apnMaxNotificationPayloadByteSize } from 'lib/push/apns-notif-creators.js';
 import type { PushUserInfo, PushInfo } from 'lib/push/send-utils.js';
-import { stringToVersionKey, getDevicesByPlatform } from 'lib/push/utils.js';
+import {
+  stringToVersionKey,
+  getDevicesByPlatform,
+  userAllowsNotif,
+} from 'lib/push/utils.js';
 import {
   type WebNotifInputData,
   webNotifInputDataValidator,
@@ -31,8 +35,6 @@
   wnsNotifInputDataValidator,
   createWNSNotification,
 } from 'lib/push/wns-notif-creators.js';
-import { oldValidUsernameRegex } from 'lib/shared/account-utils.js';
-import { isUserMentioned } from 'lib/shared/mention-utils.js';
 import {
   createMessageInfo,
   shimUnsupportedRawMessageInfos,
@@ -245,36 +247,24 @@
   const parentThreadInfo = threadInfo.parentThreadID
     ? threadInfos[threadInfo.parentThreadID]
     : null;
-  const updateBadge = threadInfo.currentUser.subscription.home;
-  const displayBanner = threadInfo.currentUser.subscription.pushNotifs;
+
   const username = userInfos[userID] && userInfos[userID].username;
 
-  let resolvedUsername;
-  if (getENSNames) {
-    const userInfosWithENSNames = await getENSNames([userInfos[userID]]);
-    resolvedUsername = userInfosWithENSNames[0].username;
-  }
+  const { notifAllowed, badgeOnly } = await userAllowsNotif(
+    {
+      ...threadInfo.currentUser.subscription,
+      role: threadInfo.currentUser.role,
+    },
+    userID,
+    newMessageInfos,
+    userInfos,
+    username,
+    getENSNames,
+  );
 
-  const userWasMentioned =
-    username &&
-    threadInfo.currentUser.role &&
-    oldValidUsernameRegex.test(username) &&
-    newMessageInfos.some(newMessageInfo => {
-      const unwrappedMessageInfo =
-        newMessageInfo.type === messageTypes.SIDEBAR_SOURCE
-          ? newMessageInfo.sourceMessage
-          : newMessageInfo;
-      return (
-        unwrappedMessageInfo.type === messageTypes.TEXT &&
-        (isUserMentioned(username, unwrappedMessageInfo.text) ||
-          (resolvedUsername &&
-            isUserMentioned(resolvedUsername, unwrappedMessageInfo.text)))
-      );
-    });
-  if (!updateBadge && !displayBanner && !userWasMentioned) {
+  if (!notifAllowed) {
     return null;
   }
-  const badgeOnly = !displayBanner && !userWasMentioned;
 
   const notifTargetUserInfo = { id: userID, username };
   const notifTexts = await notifTextsForMessageInfo(
@@ -369,6 +359,7 @@
               newRawMessageInfos: shimmedNewRawMessageInfos,
               threadID: threadInfo.id,
               collapseKey: notifInfo.collapseKey,
+              badgeOnly,
               unreadCount,
               platformDetails,
               notifID: dbID,
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
@@ -38,6 +38,7 @@
   +newRawMessageInfos: RawMessageInfo[],
   +threadID: string,
   +collapseKey: ?string,
+  +badgeOnly: boolean,
   +unreadCount?: number,
   +platformDetails: PlatformDetails,
 }>;
@@ -49,6 +50,7 @@
     newRawMessageInfos: t.list(rawMessageInfoValidator),
     threadID: tID,
     collapseKey: t.maybe(t.String),
+    badgeOnly: t.Boolean,
     unreadCount: t.maybe(t.Number),
     platformDetails: tPlatformDetails,
   });
@@ -75,6 +77,7 @@
     newRawMessageInfos,
     threadID,
     collapseKey,
+    badgeOnly,
     unreadCount,
     platformDetails,
     notifID,
@@ -128,7 +131,7 @@
   notification.data = {
     ...notification.data,
     id,
-    badgeOnly: '0',
+    badgeOnly: badgeOnly ? '1' : '0',
   };
 
   const messageInfos = JSON.stringify(newRawMessageInfos);
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
@@ -32,14 +32,12 @@
 
 export type APNsNotifInputData = {
   ...CommonNativeNotifInputData,
-  +badgeOnly: boolean,
   +uniqueID: string,
 };
 
 export const apnsNotifInputDataValidator: TInterface<APNsNotifInputData> =
   tShape<APNsNotifInputData>({
     ...commonNativeNotifInputDataValidator.meta.props,
-    badgeOnly: t.Boolean,
     uniqueID: t.String,
   });
 
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
@@ -9,6 +9,7 @@
   stringToVersionKey,
   getDevicesByPlatform,
   generateNotifUserInfoPromise,
+  userAllowsNotif,
 } from './utils.js';
 import { createWebNotification } from './web-notif-creators.js';
 import { createWNSNotification } from './wns-notif-creators.js';
@@ -44,6 +45,7 @@
   SenderDeviceDescriptor,
   EncryptedNotifUtilsAPI,
 } from '../types/notif-types.js';
+import type { ThreadSubscription } from '../types/subscription-types.js';
 import type { RawThreadInfos } from '../types/thread-types.js';
 import type { UserInfos } from '../types/user-types.js';
 import { type GetENSNames } from '../utils/ens-helpers.js';
@@ -56,17 +58,27 @@
   +cryptoID: string,
 };
 
+export type ThreadSubscriptionWithRole = $ReadOnly<{
+  ...ThreadSubscription,
+  role: ?string,
+}>;
+
 export type PushUserInfo = {
   +devices: $ReadOnlyArray<Device>,
   +messageInfos: RawMessageInfo[],
   +messageDatas: MessageData[],
+  +subscriptions?: {
+    +[threadID: string]: ThreadSubscriptionWithRole,
+  },
 };
 
 export type PushInfo = { +[userID: string]: PushUserInfo };
 
 type PushUserThreadInfo = {
   +devices: $ReadOnlyArray<Device>,
-  +threadIDs: Set<string>,
+  +threadsWithSubscriptions: {
+    [threadID: string]: ThreadSubscriptionWithRole,
+  },
 };
 
 function identityPlatformDetailsToPlatformDetails(
@@ -123,13 +135,15 @@
     for (const memberInfo of threadInfo.members) {
       if (
         !isMemberActive(memberInfo) ||
-        !hasPermission(memberInfo.permissions, 'visible')
+        !hasPermission(memberInfo.permissions, 'visible') ||
+        !memberInfo.subscription
       ) {
         continue;
       }
 
       if (pushUserThreadInfos[memberInfo.id]) {
-        pushUserThreadInfos[memberInfo.id].threadIDs.add(threadID);
+        pushUserThreadInfos[memberInfo.id].threadsWithSubscriptions[threadID] =
+          { ...memberInfo.subscription, role: memberInfo.role };
         continue;
       }
 
@@ -152,7 +166,9 @@
 
       pushUserThreadInfos[memberInfo.id] = {
         devices,
-        threadIDs: new Set([threadID]),
+        threadsWithSubscriptions: {
+          [threadID]: { ...memberInfo.subscription, role: memberInfo.role },
+        },
       };
     }
   }
@@ -163,28 +179,47 @@
   for (const userID in pushUserThreadInfos) {
     const pushUserThreadInfo = pushUserThreadInfos[userID];
 
-    userPushInfoPromises[userID] = generateNotifUserInfoPromise(
-      pushTypes.NOTIF,
-      pushUserThreadInfo.devices,
-      newMessageInfos,
-      messageDatas,
-      threadsToMessageIndices,
-      pushUserThreadInfo.threadIDs,
-      new Set(),
-      (messageID: string) => (async () => messageInfos[messageID])(),
-      userID,
-    );
-    userRescindInfoPromises[userID] = generateNotifUserInfoPromise(
-      pushTypes.RESCIND,
-      pushUserThreadInfo.devices,
-      newMessageInfos,
-      messageDatas,
-      threadsToMessageIndices,
-      pushUserThreadInfo.threadIDs,
-      new Set(),
-      (messageID: string) => (async () => messageInfos[messageID])(),
-      userID,
-    );
+    userPushInfoPromises[userID] = (async () => {
+      const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise(
+        pushTypes.NOTIF,
+        pushUserThreadInfo.devices,
+        newMessageInfos,
+        messageDatas,
+        threadsToMessageIndices,
+        Object.keys(pushUserThreadInfo.threadsWithSubscriptions),
+        new Set(),
+        (messageID: string) => (async () => messageInfos[messageID])(),
+        userID,
+      );
+      if (!pushInfosWithoutSubscriptions) {
+        return null;
+      }
+      return {
+        ...pushInfosWithoutSubscriptions,
+        subscriptions: pushUserThreadInfo.threadsWithSubscriptions,
+      };
+    })();
+
+    userRescindInfoPromises[userID] = (async () => {
+      const pushInfosWithoutSubscriptions = await generateNotifUserInfoPromise(
+        pushTypes.RESCIND,
+        pushUserThreadInfo.devices,
+        newMessageInfos,
+        messageDatas,
+        threadsToMessageIndices,
+        Object.keys(pushUserThreadInfo.threadsWithSubscriptions),
+        new Set(),
+        (messageID: string) => (async () => messageInfos[messageID])(),
+        userID,
+      );
+      if (!pushInfosWithoutSubscriptions) {
+        return null;
+      }
+      return {
+        ...pushInfosWithoutSubscriptions,
+        subscriptions: pushUserThreadInfo.threadsWithSubscriptions,
+      };
+    })();
   }
 
   const [pushInfo, rescindInfo] = await Promise.all([
@@ -202,13 +237,19 @@
   rawMessageInfos: $ReadOnlyArray<RawMessageInfo>,
   userID: string,
   threadInfos: { +[id: string]: ThreadInfo },
+  subscriptions: ?{ +[threadID: string]: ThreadSubscriptionWithRole },
   userInfos: UserInfos,
   getENSNames: ?GetENSNames,
   getFCNames: ?GetFCNames,
 ): Promise<?{
   +notifTexts: ResolvedNotifTexts,
   +newRawMessageInfos: $ReadOnlyArray<RawMessageInfo>,
+  +badgeOnly: boolean,
 }> {
+  if (!subscriptions) {
+    return null;
+  }
+
   const hydrateMessageInfo = (rawMessageInfo: RawMessageInfo) =>
     createMessageInfo(rawMessageInfo, userID, userInfos, threadInfos);
 
@@ -233,9 +274,25 @@
     ? threadInfos[threadInfo.parentThreadID]
     : null;
 
-  // TODO: Using types from @Ashoat check ThreadSubscription and mentioning
+  const subscription = subscriptions[threadID];
+  if (!subscription) {
+    return null;
+  }
 
   const username = userInfos[userID] && userInfos[userID].username;
+  const { notifAllowed, badgeOnly } = await userAllowsNotif(
+    subscription,
+    userID,
+    newMessageInfos,
+    userInfos,
+    username,
+    getENSNames,
+  );
+
+  if (!notifAllowed) {
+    return null;
+  }
+
   const notifTargetUserInfo = { id: userID, username };
   const notifTexts = await notifTextsForMessageInfo(
     newMessageInfos,
@@ -249,7 +306,7 @@
     return null;
   }
 
-  return { notifTexts, newRawMessageInfos };
+  return { notifTexts, newRawMessageInfos, badgeOnly };
 }
 
 async function buildNotifsForUserDevices(
@@ -258,6 +315,7 @@
   rawMessageInfos: $ReadOnlyArray<RawMessageInfo>,
   userID: string,
   threadInfos: { +[id: string]: ThreadInfo },
+  subscriptions: ?{ +[threadID: string]: ThreadSubscriptionWithRole },
   userInfos: UserInfos,
   getENSNames: ?GetENSNames,
   getFCNames: ?GetFCNames,
@@ -270,6 +328,7 @@
     rawMessageInfos,
     userID,
     threadInfos,
+    subscriptions,
     userInfos,
     getENSNames,
     getFCNames,
@@ -279,7 +338,8 @@
     return null;
   }
 
-  const { notifTexts, newRawMessageInfos } = notifTextWithNewRawMessageInfos;
+  const { notifTexts, newRawMessageInfos, badgeOnly } =
+    notifTextWithNewRawMessageInfos;
   const [{ threadID }] = newRawMessageInfos;
 
   const promises: Array<
@@ -311,7 +371,7 @@
                 newRawMessageInfos: shimmedNewRawMessageInfos,
                 threadID,
                 collapseKey: undefined,
-                badgeOnly: false,
+                badgeOnly,
                 unreadCount: undefined,
                 platformDetails,
                 uniqueID: uuidv4(),
@@ -352,6 +412,7 @@
                 newRawMessageInfos: shimmedNewRawMessageInfos,
                 threadID,
                 collapseKey: undefined,
+                badgeOnly,
                 unreadCount: undefined,
                 platformDetails,
                 notifID: uuidv4(),
@@ -394,7 +455,7 @@
                 newRawMessageInfos: shimmedNewRawMessageInfos,
                 threadID,
                 collapseKey: undefined,
-                badgeOnly: false,
+                badgeOnly,
                 unreadCount: undefined,
                 platformDetails,
                 uniqueID: uuidv4(),
@@ -534,6 +595,7 @@
             [rawMessageInfos],
             userID,
             threadInfos,
+            pushInfo[userID].subscriptions,
             userInfos,
             getENSNames,
             getFCNames,
diff --git a/lib/push/utils.js b/lib/push/utils.js
--- a/lib/push/utils.js
+++ b/lib/push/utils.js
@@ -1,15 +1,21 @@
 // @flow
 import invariant from 'invariant';
 
-import type { Device } from './send-utils.js';
+import type { Device, ThreadSubscriptionWithRole } from './send-utils.js';
+import { oldValidUsernameRegex } from '../shared/account-utils.js';
+import { isUserMentioned } from '../shared/mention-utils.js';
 import { type PushType } from '../shared/messages/message-spec.js';
 import { messageSpecs } from '../shared/messages/message-specs.js';
 import type { Platform } from '../types/device-types.js';
-import {
-  type MessageData,
-  type RawMessageInfo,
+import { messageTypes } from '../types/message-types-enum.js';
+import type {
+  MessageData,
+  RawMessageInfo,
+  MessageInfo,
 } from '../types/message-types.js';
 import type { NotificationTargetDevice } from '../types/notif-types.js';
+import type { GlobalUserInfo, UserInfo } from '../types/user-types.js';
+import type { GetENSNames } from '../utils/ens-helpers.js';
 
 export type VersionKey = {
   +codeVersion: number,
@@ -88,7 +94,7 @@
   newMessageInfos: $ReadOnlyArray<RawMessageInfo>,
   messageDatas: $ReadOnlyArray<MessageData>,
   threadsToMessageIndices: $ReadOnlyMap<string, number[]>,
-  threadIDs: $ReadOnlySet<string>,
+  threadIDs: $ReadOnlyArray<string>,
   userNotMemberOfSubthreads: Set<string>,
   fetchMessageInfoByID: (messageID: string) => Promise<any>,
   userID: string,
@@ -158,9 +164,53 @@
   };
 }
 
+async function userAllowsNotif(
+  subscription: ThreadSubscriptionWithRole,
+  userID: string,
+  newMessageInfos: $ReadOnlyArray<MessageInfo>,
+  userInfos: { +[string]: UserInfo | GlobalUserInfo },
+  username: ?string,
+  getENSNames: ?GetENSNames,
+): Promise<{
+  +notifAllowed: boolean,
+  +badgeOnly: boolean,
+}> {
+  const updateBadge = subscription.home;
+  const displayBanner = subscription.pushNotifs;
+
+  let resolvedUsername;
+  if (getENSNames) {
+    const userInfosWithENSNames = await getENSNames([userInfos[userID]]);
+    resolvedUsername = userInfosWithENSNames[0].username;
+  }
+
+  const userWasMentioned =
+    username &&
+    subscription.role &&
+    oldValidUsernameRegex.test(username) &&
+    newMessageInfos.some(newMessageInfo => {
+      const unwrappedMessageInfo =
+        newMessageInfo.type === messageTypes.SIDEBAR_SOURCE
+          ? newMessageInfo.sourceMessage
+          : newMessageInfo;
+      return (
+        unwrappedMessageInfo.type === messageTypes.TEXT &&
+        (isUserMentioned(username, unwrappedMessageInfo.text) ||
+          (resolvedUsername &&
+            isUserMentioned(resolvedUsername, unwrappedMessageInfo.text)))
+      );
+    });
+
+  const notifAllowed = !!updateBadge || !!displayBanner || !!userWasMentioned;
+  const badgeOnly = !displayBanner && !userWasMentioned;
+
+  return { notifAllowed, badgeOnly };
+}
+
 export {
   stringToVersionKey,
   versionKeyToString,
   getDevicesByPlatform,
   generateNotifUserInfoPromise,
+  userAllowsNotif,
 };
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -45,6 +45,7 @@
   MemberInfoWithPermissions,
   RoleInfo,
   ThreadInfo,
+  MinimallyEncodedThickMemberInfo,
 } from '../types/minimally-encoded-thread-permissions-types.js';
 import {
   decodeMinimallyEncodedRoleInfo,
@@ -223,7 +224,9 @@
   );
 }
 
-function isMemberActive(memberInfo: MemberInfoWithPermissions): boolean {
+function isMemberActive(
+  memberInfo: MemberInfoWithPermissions | MinimallyEncodedThickMemberInfo,
+): boolean {
   const role = memberInfo.role;
   return role !== null && role !== undefined;
 }
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
@@ -92,7 +92,7 @@
   +prefix?: string,
   +threadID: string,
   +collapseID?: string,
-  +badgeOnly?: '0',
+  +badgeOnly?: '0' | '1',
   +encryptionFailed?: '1',
 }>;