diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts
index d45d076a8adb..43ddff2508ee 100644
--- a/src/libs/Notification/PushNotification/index.native.ts
+++ b/src/libs/Notification/PushNotification/index.native.ts
@@ -10,7 +10,7 @@ import NotificationType from './NotificationType';
 import type {ClearNotifications, Deregister, Init, OnReceived, OnSelected, Register} from './types';
 import type PushNotificationType from './types';
 
-type NotificationEventActionCallback = (data: NotificationData) => void;
+type NotificationEventActionCallback = (data: NotificationData) => Promise<void>;
 
 type NotificationEventActionMap = Partial<Record<EventType, Record<string, NotificationEventActionCallback>>>;
 
@@ -56,7 +56,13 @@ function pushNotificationEventCallback(eventType: EventType, notification: PushP
         });
         return;
     }
-    action(data);
+
+    /**
+     * The action callback should return a promise. It's very important we return that promise so that
+     * when these callbacks are run in Android's background process (via Headless JS), the process waits
+     * for the promise to resolve before quitting
+     */
+    return action(data);
 }
 
 /**
@@ -83,15 +89,11 @@ function refreshNotificationOptInStatus() {
  */
 const init: Init = () => {
     // Setup event listeners
-    Airship.addListener(EventType.PushReceived, (notification) => {
-        pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload);
-    });
+    Airship.addListener(EventType.PushReceived, (notification) => pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload));
 
     // Note: the NotificationResponse event has a nested PushReceived event,
     // so event.notification refers to the same thing as notification above ^
-    Airship.addListener(EventType.NotificationResponse, (event) => {
-        pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload);
-    });
+    Airship.addListener(EventType.NotificationResponse, (event) => pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload));
 
     // Keep track of which users have enabled push notifications via an NVP.
     Airship.addListener(EventType.PushNotificationStatusChangedStatus, refreshNotificationOptInStatus);
diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts
index 36051fa35c56..cd34807a5735 100644
--- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts
+++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts
@@ -1,5 +1,5 @@
 import Onyx from 'react-native-onyx';
-import * as OnyxUpdates from '@libs/actions/OnyxUpdates';
+import applyOnyxUpdatesReliably from '@libs/actions/applyOnyxUpdatesReliably';
 import * as ActiveClientManager from '@libs/ActiveClientManager';
 import Log from '@libs/Log';
 import Navigation from '@libs/Navigation/Navigation';
@@ -26,35 +26,51 @@ Onyx.connect({
     },
 });
 
+function getLastUpdateIDAppliedToClient(): Promise<number> {
+    return new Promise((resolve) => {
+        Onyx.connect({
+            key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
+            callback: (value) => resolve(value ?? 0),
+        });
+    });
+}
+
 /**
  * Setup reportComment push notification callbacks.
  */
 export default function subscribeToReportCommentPushNotifications() {
     PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => {
+        Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID});
+
         if (!ActiveClientManager.isClientTheLeader()) {
             Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client');
-            return;
+            return Promise.resolve();
         }
-        Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID});
-
-        if (onyxData && lastUpdateID && previousUpdateID) {
-            Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0});
 
-            const updates: OnyxUpdatesFromServer = {
-                type: CONST.ONYX_UPDATE_TYPES.AIRSHIP,
-                lastUpdateID,
-                previousUpdateID,
-                updates: [
-                    {
-                        eventType: 'eventType',
-                        data: onyxData,
-                    },
-                ],
-            };
-            OnyxUpdates.applyOnyxUpdatesReliably(updates);
-        } else {
-            Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0});
+        if (!onyxData || !lastUpdateID || !previousUpdateID) {
+            Log.hmmm("[PushNotification] didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0});
+            return Promise.resolve();
         }
+
+        Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0});
+        const updates: OnyxUpdatesFromServer = {
+            type: CONST.ONYX_UPDATE_TYPES.AIRSHIP,
+            lastUpdateID,
+            previousUpdateID,
+            updates: [
+                {
+                    eventType: 'eventType',
+                    data: onyxData,
+                },
+            ],
+        };
+
+        /**
+         * When this callback runs in the background on Android (via Headless JS), no other Onyx.connect callbacks will run. This means that
+         * lastUpdateIDAppliedToClient will NOT be populated in other libs. To workaround this, we manually read the value here
+         * and pass it as a param
+         */
+        return getLastUpdateIDAppliedToClient().then((lastUpdateIDAppliedToClient) => applyOnyxUpdatesReliably(updates, true, lastUpdateIDAppliedToClient));
     });
 
     // Open correct report when push notification is clicked
@@ -96,5 +112,7 @@ export default function subscribeToReportCommentPushNotifications() {
                     }
                 });
             });
+
+        return Promise.resolve();
     });
 }
diff --git a/src/libs/Notification/PushNotification/types.ts b/src/libs/Notification/PushNotification/types.ts
index 4399c10b4a95..cf7e54abd094 100644
--- a/src/libs/Notification/PushNotification/types.ts
+++ b/src/libs/Notification/PushNotification/types.ts
@@ -5,8 +5,8 @@ import type NotificationType from './NotificationType';
 type Init = () => void;
 type Register = (notificationID: string | number) => void;
 type Deregister = () => void;
-type OnReceived = <T extends ValueOf<typeof NotificationType>>(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void;
-type OnSelected = <T extends ValueOf<typeof NotificationType>>(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void;
+type OnReceived = <T extends ValueOf<typeof NotificationType>>(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise<void>) => void;
+type OnSelected = <T extends ValueOf<typeof NotificationType>>(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise<void>) => void;
 type ClearNotifications = () => void;
 
 type PushNotification = {
diff --git a/src/libs/actions/OnyxUpdateManager.ts b/src/libs/actions/OnyxUpdateManager.ts
index f1f26e259ab1..8d7f299160be 100644
--- a/src/libs/actions/OnyxUpdateManager.ts
+++ b/src/libs/actions/OnyxUpdateManager.ts
@@ -1,9 +1,11 @@
+import type {OnyxEntry} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import * as ActiveClientManager from '@libs/ActiveClientManager';
 import Log from '@libs/Log';
 import * as SequentialQueue from '@libs/Network/SequentialQueue';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import type {OnyxUpdatesFromServer} from '@src/types/onyx';
 import * as App from './App';
 import * as OnyxUpdates from './OnyxUpdates';
 
@@ -36,83 +38,94 @@ Onyx.connect({
     },
 });
 
-export default () => {
-    console.debug('[OnyxUpdateManager] Listening for updates from the server');
-    Onyx.connect({
-        key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER,
-        callback: (value) => {
-            // When there's no value, there's nothing to process, so let's return early.
-            if (!value) {
-                return;
-            }
-            // If isLoadingApp is positive it means that OpenApp command hasn't finished yet, and in that case
-            // we don't have base state of the app (reports, policies, etc) setup. If we apply this update,
-            // we'll only have them overriten by the openApp response. So let's skip it and return.
-            if (isLoadingApp) {
-                // When ONYX_UPDATES_FROM_SERVER is set, we pause the queue. Let's unpause
-                // it so the app is not stuck forever without processing requests.
-                SequentialQueue.unpause();
-                console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`);
-                return;
-            }
-            // This key is shared across clients, thus every client/tab will have a copy and try to execute this method.
-            // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute
-            // several duplicated requests that are not controlled by the SequentialQueue.
-            if (!ActiveClientManager.isClientTheLeader()) {
-                return;
-            }
+/**
+ *
+ * @param onyxUpdatesFromServer
+ * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient
+ * @returns
+ */
+function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry<OnyxUpdatesFromServer>, clientLastUpdateID = 0) {
+    // When there's no value, there's nothing to process, so let's return early.
+    if (!onyxUpdatesFromServer) {
+        return;
+    }
+    // If isLoadingApp is positive it means that OpenApp command hasn't finished yet, and in that case
+    // we don't have base state of the app (reports, policies, etc) setup. If we apply this update,
+    // we'll only have them overriten by the openApp response. So let's skip it and return.
+    if (isLoadingApp) {
+        // When ONYX_UPDATES_FROM_SERVER is set, we pause the queue. Let's unpause
+        // it so the app is not stuck forever without processing requests.
+        SequentialQueue.unpause();
+        console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`);
+        return;
+    }
+    // This key is shared across clients, thus every client/tab will have a copy and try to execute this method.
+    // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute
+    // several duplicated requests that are not controlled by the SequentialQueue.
+    if (!ActiveClientManager.isClientTheLeader()) {
+        return;
+    }
 
-            // Since we used the same key that used to store another object, let's confirm that the current object is
-            // following the new format before we proceed. If it isn't, then let's clear the object in Onyx.
-            if (
-                !(typeof value === 'object' && !!value) ||
-                !('type' in value) ||
-                (!(value.type === CONST.ONYX_UPDATE_TYPES.HTTPS && value.request && value.response) &&
-                    !((value.type === CONST.ONYX_UPDATE_TYPES.PUSHER || value.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && value.updates))
-            ) {
-                console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue');
-                Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null);
-                SequentialQueue.unpause();
-                return;
-            }
+    // Since we used the same key that used to store another object, let's confirm that the current object is
+    // following the new format before we proceed. If it isn't, then let's clear the object in Onyx.
+    if (
+        !(typeof onyxUpdatesFromServer === 'object' && !!onyxUpdatesFromServer) ||
+        !('type' in onyxUpdatesFromServer) ||
+        (!(onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.HTTPS && onyxUpdatesFromServer.request && onyxUpdatesFromServer.response) &&
+            !((onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.PUSHER || onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && onyxUpdatesFromServer.updates))
+    ) {
+        console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue');
+        Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null);
+        SequentialQueue.unpause();
+        return;
+    }
 
-            const updateParams = value;
-            const lastUpdateIDFromServer = value.lastUpdateID;
-            const previousUpdateIDFromServer = value.previousUpdateID;
+    const updateParams = onyxUpdatesFromServer;
+    const lastUpdateIDFromServer = onyxUpdatesFromServer.lastUpdateID;
+    const previousUpdateIDFromServer = onyxUpdatesFromServer.previousUpdateID;
+    const lastUpdateIDFromClient = clientLastUpdateID || lastUpdateIDAppliedToClient;
 
-            // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient
-            // we need to perform one of the 2 possible cases:
-            //
-            // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before
-            // fully migrating to the reliable updates mode.
-            // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it
-            // needs to fetch those.
-            let canUnpauseQueuePromise;
+    // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient
+    // we need to perform one of the 2 possible cases:
+    //
+    // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before
+    // fully migrating to the reliable updates mode.
+    // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it
+    // needs to fetch those.
+    let canUnpauseQueuePromise;
 
-            // The flow below is setting the promise to a reconnect app to address flow (1) explained above.
-            if (!lastUpdateIDAppliedToClient) {
-                Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process');
+    // The flow below is setting the promise to a reconnect app to address flow (1) explained above.
+    if (!lastUpdateIDFromClient) {
+        Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process');
 
-                // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request.
-                canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates();
-            } else {
-                // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above.
-                console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDAppliedToClient} so fetching incremental updates`);
-                Log.info('Gap detected in update IDs from server so fetching incremental updates', true, {
-                    lastUpdateIDFromServer,
-                    previousUpdateIDFromServer,
-                    lastUpdateIDAppliedToClient,
-                });
-                canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDAppliedToClient, previousUpdateIDFromServer);
-            }
+        // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request.
+        canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates();
+    } else {
+        // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above.
+        console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDFromClient} so fetching incremental updates`);
+        Log.info('Gap detected in update IDs from server so fetching incremental updates', true, {
+            lastUpdateIDFromServer,
+            previousUpdateIDFromServer,
+            lastUpdateIDFromClient,
+        });
+        canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDFromClient, previousUpdateIDFromServer);
+    }
 
-            canUnpauseQueuePromise.finally(() => {
-                OnyxUpdates.apply(updateParams).finally(() => {
-                    console.debug('[OnyxUpdateManager] Done applying all updates');
-                    Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null);
-                    SequentialQueue.unpause();
-                });
-            });
-        },
+    canUnpauseQueuePromise.finally(() => {
+        OnyxUpdates.apply(updateParams).finally(() => {
+            console.debug('[OnyxUpdateManager] Done applying all updates');
+            Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null);
+            SequentialQueue.unpause();
+        });
+    });
+}
+
+export default () => {
+    console.debug('[OnyxUpdateManager] Listening for updates from the server');
+    Onyx.connect({
+        key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER,
+        callback: (value) => handleOnyxUpdateGap(value),
     });
 };
+
+export {handleOnyxUpdateGap};
diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts
index bb486d97b33b..04656f1adfec 100644
--- a/src/libs/actions/OnyxUpdates.ts
+++ b/src/libs/actions/OnyxUpdates.ts
@@ -132,29 +132,23 @@ function saveUpdateInformation(updateParams: OnyxUpdatesFromServer) {
  * This function will receive the previousUpdateID from any request/pusher update that has it, compare to our current app state
  * and return if an update is needed
  * @param previousUpdateID The previousUpdateID contained in the response object
+ * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient
  */
-function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean {
+function doesClientNeedToBeUpdated(previousUpdateID = 0, clientLastUpdateID = 0): boolean {
     // If no previousUpdateID is sent, this is not a WRITE request so we don't need to update our current state
     if (!previousUpdateID) {
         return false;
     }
 
-    // If we don't have any value in lastUpdateIDAppliedToClient, this is the first time we're receiving anything, so we need to do a last reconnectApp
-    if (!lastUpdateIDAppliedToClient) {
+    const lastUpdateIDFromClient = clientLastUpdateID || lastUpdateIDAppliedToClient;
+
+    // If we don't have any value in lastUpdateIDFromClient, this is the first time we're receiving anything, so we need to do a last reconnectApp
+    if (!lastUpdateIDFromClient) {
         return true;
     }
 
-    return lastUpdateIDAppliedToClient < previousUpdateID;
-}
-
-function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer) {
-    const previousUpdateID = Number(updates.previousUpdateID) || 0;
-    if (!doesClientNeedToBeUpdated(previousUpdateID)) {
-        apply(updates);
-        return;
-    }
-    saveUpdateInformation(updates);
+    return lastUpdateIDFromClient < previousUpdateID;
 }
 
 // eslint-disable-next-line import/prefer-default-export
-export {saveUpdateInformation, doesClientNeedToBeUpdated, apply, applyOnyxUpdatesReliably};
+export {saveUpdateInformation, doesClientNeedToBeUpdated, apply};
diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts
index dcd6e025e23b..4487dcf407c4 100644
--- a/src/libs/actions/User.ts
+++ b/src/libs/actions/User.ts
@@ -44,8 +44,8 @@ import type ReportAction from '@src/types/onyx/ReportAction';
 import type {OriginalMessage} from '@src/types/onyx/ReportAction';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import type {EmptyObject} from '@src/types/utils/EmptyObject';
+import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably';
 import * as Link from './Link';
-import * as OnyxUpdates from './OnyxUpdates';
 import * as Report from './Report';
 import * as Session from './Session';
 
@@ -597,7 +597,7 @@ function subscribeToUserEvents() {
             updates: pushJSON.updates ?? [],
             previousUpdateID: Number(pushJSON.previousUpdateID || 0),
         };
-        OnyxUpdates.applyOnyxUpdatesReliably(updates);
+        applyOnyxUpdatesReliably(updates);
     });
 
     // Handles Onyx updates coming from Pusher through the mega multipleEvents.
diff --git a/src/libs/actions/applyOnyxUpdatesReliably.ts b/src/libs/actions/applyOnyxUpdatesReliably.ts
new file mode 100644
index 000000000000..17754712cdc8
--- /dev/null
+++ b/src/libs/actions/applyOnyxUpdatesReliably.ts
@@ -0,0 +1,26 @@
+import type {OnyxUpdatesFromServer} from '@src/types/onyx';
+import {handleOnyxUpdateGap} from './OnyxUpdateManager';
+import * as OnyxUpdates from './OnyxUpdates';
+
+/**
+ * Checks for and handles gaps of onyx updates between the client and the given server updates before applying them
+ *
+ * This is in it's own lib to fix a dependency cycle from OnyxUpdateManager
+ *
+ * @param updates
+ * @param shouldRunSync
+ * @returns
+ */
+export default function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false, clientLastUpdateID = 0) {
+    const previousUpdateID = Number(updates.previousUpdateID) || 0;
+    if (!OnyxUpdates.doesClientNeedToBeUpdated(previousUpdateID, clientLastUpdateID)) {
+        OnyxUpdates.apply(updates);
+        return;
+    }
+
+    if (shouldRunSync) {
+        handleOnyxUpdateGap(updates, clientLastUpdateID);
+    } else {
+        OnyxUpdates.saveUpdateInformation(updates);
+    }
+}
