diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/IamFetchReadyCondition.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/IamFetchReadyCondition.kt
new file mode 100644
index 0000000000..d43e3c989a
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/IamFetchReadyCondition.kt
@@ -0,0 +1,41 @@
+package com.onesignal.common.consistency
+
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.ICondition
+import com.onesignal.common.consistency.models.IConsistencyKeyEnum
+
+/**
+ * Used for read your write consistency when fetching In-App Messages.
+ *
+ * Params:
+ *  key : String - the index of the RYW token map
+ */
+class IamFetchReadyCondition(
+    private val key: String,
+) : ICondition {
+    companion object {
+        const val ID = "IamFetchReadyCondition"
+    }
+
+    override val id: String
+        get() = ID
+
+    override fun isMet(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String>>): Boolean {
+        val tokenMap = indexedTokens[key] ?: return false
+        val userUpdateTokenSet = tokenMap[IamFetchRywTokenKey.USER] != null
+
+        /**
+         * We always update the session count so we know we will have a userUpdateToken. We don't
+         * necessarily make a subscriptionUpdate call on every session. The following logic
+         * doesn't consider tokenMap[IamFetchRywTokenKey.SUBSCRIPTION] for this reason. This doesn't
+         * mean it isn't considered if present when doing the token comparison.
+         */
+        return userUpdateTokenSet
+    }
+
+    override fun getNewestToken(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String?>>): String? {
+        val tokenMap = indexedTokens[key] ?: return null
+        // maxOrNull compares lexicographically
+        return listOfNotNull(tokenMap[IamFetchRywTokenKey.USER], tokenMap[IamFetchRywTokenKey.SUBSCRIPTION]).maxOrNull()
+    }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/enums/IamFetchRywTokenKey.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/enums/IamFetchRywTokenKey.kt
new file mode 100644
index 0000000000..3f31c9a98f
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/enums/IamFetchRywTokenKey.kt
@@ -0,0 +1,12 @@
+package com.onesignal.common.consistency.enums
+
+import com.onesignal.common.consistency.models.IConsistencyKeyEnum
+
+/**
+ * Each enum is a key that we use to keep track of read-your-write tokens.
+ * Although the enums are named with "UPDATE", they serve as keys for tokens from both PATCH & POST
+ */
+enum class IamFetchRywTokenKey : IConsistencyKeyEnum {
+    USER,
+    SUBSCRIPTION,
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/impl/ConsistencyManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/impl/ConsistencyManager.kt
new file mode 100644
index 0000000000..139c06e3be
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/impl/ConsistencyManager.kt
@@ -0,0 +1,92 @@
+package com.onesignal.common.consistency.impl
+
+import com.onesignal.common.consistency.models.ICondition
+import com.onesignal.common.consistency.models.IConsistencyKeyEnum
+import com.onesignal.common.consistency.models.IConsistencyManager
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+/**
+ * Manages read-your-write tokens for more accurate segment membership
+ * calculation. Uses customizable conditions that block retrieval of the newest token until met.
+ *
+ * Usage:
+ *  val consistencyManager = ConsistencyManager<MyEnum>()
+ *  val updateConditionDeferred = consistencyManager.registerCondition(MyCustomCondition())
+ *  val rywToken = updateConditionDeferred.await()
+ */
+class ConsistencyManager : IConsistencyManager {
+    private val mutex = Mutex()
+    private val indexedTokens: MutableMap<String, MutableMap<IConsistencyKeyEnum, String>> = mutableMapOf()
+    private val conditions: MutableList<Pair<ICondition, CompletableDeferred<String?>>> =
+        mutableListOf()
+
+    /**
+     * Set method to update the token based on the key.
+     *  Params:
+     *      id: String - the index of the token map (e.g. onesignalId)
+     *      key: K - corresponds to the operation for which we have a read-your-write token
+     *      value: String? - the token (read-your-write token)
+     */
+    override suspend fun setRywToken(
+        id: String,
+        key: IConsistencyKeyEnum,
+        value: String,
+    ) {
+        mutex.withLock {
+            val rywTokens = indexedTokens.getOrPut(id) { mutableMapOf() }
+            rywTokens[key] = value
+            checkConditionsAndComplete()
+        }
+    }
+
+    /**
+     * Register a condition with its corresponding deferred action. Returns a deferred condition.
+     */
+    override suspend fun registerCondition(condition: ICondition): CompletableDeferred<String?> {
+        mutex.withLock {
+            val deferred = CompletableDeferred<String?>()
+            val pair = Pair(condition, deferred)
+            conditions.add(pair)
+            checkConditionsAndComplete()
+            return deferred
+        }
+    }
+
+    override suspend fun resolveConditionsWithID(id: String) {
+        val completedConditions = mutableListOf<Pair<ICondition, CompletableDeferred<String?>>>()
+
+        for ((condition, deferred) in conditions) {
+            if (condition.id == id) {
+                if (!deferred.isCompleted) {
+                    deferred.complete(null)
+                }
+            }
+            completedConditions.add(Pair(condition, deferred))
+        }
+
+        // Remove completed conditions from the list
+        conditions.removeAll(completedConditions)
+    }
+
+    /**
+     * IMPORTANT: calling code should be protected by mutex to avoid potential inconsistencies
+     */
+    private fun checkConditionsAndComplete() {
+        val completedConditions = mutableListOf<Pair<ICondition, CompletableDeferred<String?>>>()
+
+        for ((condition, deferred) in conditions) {
+            if (condition.isMet(indexedTokens)) {
+                val newestToken = condition.getNewestToken(indexedTokens)
+                if (!deferred.isCompleted) {
+                    deferred.complete(newestToken)
+                }
+                completedConditions.add(Pair(condition, deferred))
+            }
+        }
+
+        // Remove completed conditions from the list
+        conditions.removeAll(completedConditions)
+    }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/ICondition.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/ICondition.kt
new file mode 100644
index 0000000000..fbb97d23bf
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/ICondition.kt
@@ -0,0 +1,21 @@
+package com.onesignal.common.consistency.models
+
+interface ICondition {
+    /**
+     * Every implementation should define a unique ID & make available via a companion object for
+     * ease of use
+     */
+    val id: String
+
+    /**
+     * Define a condition that "unblocks" execution
+     * e.g. we have token (A && B) || A
+     */
+    fun isMet(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String>>): Boolean
+
+    /**
+     * Used to process tokens according to their format & return the newest token.
+     * e.g. numeric strings would be compared differently from JWT tokens
+     */
+    fun getNewestToken(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String?>>): String?
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyKeyEnum.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyKeyEnum.kt
new file mode 100644
index 0000000000..058561232a
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyKeyEnum.kt
@@ -0,0 +1,3 @@
+package com.onesignal.common.consistency.models
+
+interface IConsistencyKeyEnum
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyManager.kt
new file mode 100644
index 0000000000..d1bba53123
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/consistency/models/IConsistencyManager.kt
@@ -0,0 +1,31 @@
+package com.onesignal.common.consistency.models
+
+import kotlinx.coroutines.CompletableDeferred
+
+interface IConsistencyManager {
+    /**
+     * Set method to update the RYW token based on the key.
+     * Params:
+     *  id: String - the index of the RYW token map (e.g., onesignalId)
+     *  key: IConsistencyKeyEnum - corresponds to the operation for which we have a read-your-write token
+     *  value: String? - the read-your-write token
+     */
+    suspend fun setRywToken(
+        id: String,
+        key: IConsistencyKeyEnum,
+        value: String,
+    )
+
+    /**
+     * Register a condition with its corresponding deferred action. Returns a deferred condition.
+     * Params:
+     *  condition: ICondition - the condition to be registered
+     * Returns: CompletableDeferred<String?> - a deferred action that completes when the condition is met
+     */
+    suspend fun registerCondition(condition: ICondition): CompletableDeferred<String?>
+
+    /**
+     * Resolve all conditions with a specific ID
+     */
+    suspend fun resolveConditionsWithID(id: String)
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt
index 30b7e93cea..f74a76bc2b 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/HttpClient.kt
@@ -184,6 +184,18 @@ internal class HttpClient(
                         }
                     }
 
+                    if (headers?.rywToken != null) {
+                        con.setRequestProperty("OneSignal-RYW-Token", headers.rywToken.toString())
+                    }
+
+                    if (headers?.retryCount != null) {
+                        con.setRequestProperty("Onesignal-Retry-Count", headers.retryCount.toString())
+                    }
+
+                    if (headers?.sessionDuration != null) {
+                        con.setRequestProperty("OneSignal-Session-Duration", headers.sessionDuration.toString())
+                    }
+
                     // Network request is made from getResponseCode()
                     httpResponse = con.responseCode
 
@@ -299,9 +311,9 @@ internal class HttpClient(
      * Reads the HTTP Retry-Limit from the response.
      */
     private fun retryLimitFromResponse(con: HttpURLConnection): Int? {
-        val retryLimitStr = con.getHeaderField("Retry-Limit")
+        val retryLimitStr = con.getHeaderField("OneSignal-Retry-Limit")
         return if (retryLimitStr != null) {
-            Logging.debug("HttpClient: Response Retry-After: $retryLimitStr")
+            Logging.debug("HttpClient: Response OneSignal-Retry-Limit: $retryLimitStr")
             retryLimitStr.toIntOrNull()
         } else {
             null
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt
index 78d870dff6..f566fd04fc 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/http/impl/OptionalHeaders.kt
@@ -1,5 +1,20 @@
 package com.onesignal.core.internal.http.impl
 
 data class OptionalHeaders(
+    /**
+     * Used as an E-Tag
+     */
     val cacheKey: String? = null,
+    /**
+     * Used for read your write consistency
+     */
+    val rywToken: String? = null,
+    /**
+     * Current retry count
+     */
+    val retryCount: Int? = null,
+    /**
+     * Used to track delay between session start and request
+     */
+    val sessionDuration: Long? = null,
 )
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
index 9669e05de5..8d2161aa65 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/session/internal/session/impl/SessionListener.kt
@@ -40,7 +40,7 @@ internal class SessionListener(
     }
 
     override fun onSessionStarted() {
-        _operationRepo.enqueue(TrackSessionStartOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId))
+        _operationRepo.enqueue(TrackSessionStartOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId), true)
     }
 
     override fun onSessionActive() {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/UserModule.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/UserModule.kt
index 7e2c5daacb..b2df99ee42 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/UserModule.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/UserModule.kt
@@ -1,5 +1,7 @@
 package com.onesignal.user
 
+import com.onesignal.common.consistency.impl.ConsistencyManager
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.modules.IModule
 import com.onesignal.common.services.ServiceBuilder
 import com.onesignal.core.internal.operations.IOperationExecutor
@@ -34,6 +36,9 @@ import com.onesignal.user.internal.subscriptions.impl.SubscriptionManager
 
 internal class UserModule : IModule {
     override fun register(builder: ServiceBuilder) {
+        // Consistency
+        builder.register<ConsistencyManager>().provides<IConsistencyManager>()
+
         // Properties
         builder.register<PropertiesModelStore>().provides<PropertiesModelStore>()
         builder.register<PropertiesModelStoreListener>().provides<IBootstrapService>()
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
index 8741b2df65..133f298e75 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
@@ -21,7 +21,7 @@ interface ISubscriptionBackendService {
         aliasLabel: String,
         aliasValue: String,
         subscription: SubscriptionObject,
-    ): String?
+    ): Pair<String, String?>?
 
     /**
      * Update an existing subscription with the properties provided.
@@ -34,7 +34,7 @@ interface ISubscriptionBackendService {
         appId: String,
         subscriptionId: String,
         subscription: SubscriptionObject,
-    )
+    ): String?
 
     /**
      * Delete an existing subscription.
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt
index a47018641d..62837fb260 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/IUserBackendService.kt
@@ -47,7 +47,7 @@ interface IUserBackendService {
         properties: PropertiesObject,
         refreshDeviceMetadata: Boolean,
         propertyiesDelta: PropertiesDeltasObject,
-    )
+    ): String?
 
     /**
      * Retrieve a user from the backend.
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
index bcb01db447..25d0e0a3e8 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
@@ -16,7 +16,7 @@ internal class SubscriptionBackendService(
         aliasLabel: String,
         aliasValue: String,
         subscription: SubscriptionObject,
-    ): String? {
+    ): Pair<String, String?>? {
         val jsonSubscription = JSONConverter.convertToJSON(subscription)
         jsonSubscription.remove("id")
         val requestJSON = JSONObject().put("subscription", jsonSubscription)
@@ -33,14 +33,19 @@ internal class SubscriptionBackendService(
             return null
         }
 
-        return subscriptionJSON.getString("id")
+        var rywToken: String? = null
+        if (responseJSON.has("ryw_token")) {
+            rywToken = responseJSON.getString("ryw_token")
+        }
+
+        return Pair(subscriptionJSON.getString("id"), rywToken)
     }
 
     override suspend fun updateSubscription(
         appId: String,
         subscriptionId: String,
         subscription: SubscriptionObject,
-    ) {
+    ): String? {
         val requestJSON =
             JSONObject()
                 .put("subscription", JSONConverter.convertToJSON(subscription))
@@ -50,6 +55,13 @@ internal class SubscriptionBackendService(
         if (!response.isSuccess) {
             throw BackendException(response.statusCode, response.payload, response.retryAfterSeconds)
         }
+
+        val responseBody = JSONObject(response.payload)
+        return if (responseBody.has("ryw_token")) {
+            responseBody.getString("ryw_token")
+        } else {
+            null
+        }
     }
 
     override suspend fun deleteSubscription(
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt
index 45492aa501..996f637e73 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/UserBackendService.kt
@@ -52,7 +52,7 @@ internal class UserBackendService(
         properties: PropertiesObject,
         refreshDeviceMetadata: Boolean,
         propertyiesDelta: PropertiesDeltasObject,
-    ) {
+    ): String? {
         val jsonObject =
             JSONObject()
                 .put("refresh_device_metadata", refreshDeviceMetadata)
@@ -70,6 +70,13 @@ internal class UserBackendService(
         if (!response.isSuccess) {
             throw BackendException(response.statusCode, response.payload, response.retryAfterSeconds)
         }
+
+        val responseBody = JSONObject(response.payload)
+        return if (responseBody.has("ryw_token")) {
+            responseBody.getString("ryw_token")
+        } else {
+            null
+        }
     }
 
     override suspend fun getUser(
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
index 73ee000745..1622d55f16 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
@@ -6,6 +6,9 @@ import com.onesignal.common.DeviceUtils
 import com.onesignal.common.NetworkUtils
 import com.onesignal.common.OneSignalUtils
 import com.onesignal.common.RootToolsInternalMethods
+import com.onesignal.common.consistency.IamFetchReadyCondition
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.common.modeling.ModelChangeTags
 import com.onesignal.core.internal.application.IApplicationService
@@ -39,6 +42,7 @@ internal class SubscriptionOperationExecutor(
     private val _configModelStore: ConfigModelStore,
     private val _buildUserService: IRebuildUserService,
     private val _newRecordState: NewRecordsState,
+    private val _consistencyManager: IConsistencyManager,
 ) : IOperationExecutor {
     override val operations: List<String>
         get() = listOf(CREATE_SUBSCRIPTION, UPDATE_SUBSCRIPTION, DELETE_SUBSCRIPTION, TRANSFER_SUBSCRIPTION)
@@ -101,7 +105,7 @@ internal class SubscriptionOperationExecutor(
                     AndroidUtils.getAppVersion(_applicationService.appContext),
                 )
 
-            val backendSubscriptionId =
+            val result =
                 _subscriptionBackend.createSubscription(
                     createOperation.appId,
                     IdentityConstants.ONESIGNAL_ID,
@@ -109,6 +113,15 @@ internal class SubscriptionOperationExecutor(
                     subscription,
                 ) ?: return ExecutionResponse(ExecutionResult.SUCCESS)
 
+            val backendSubscriptionId = result.first
+            val rywToken = result.second
+
+            if (rywToken != null) {
+                _consistencyManager.setRywToken(createOperation.onesignalId, IamFetchRywTokenKey.SUBSCRIPTION, rywToken)
+            } else {
+                _consistencyManager.resolveConditionsWithID(IamFetchReadyCondition.ID)
+            }
+
             // update the subscription model with the new ID, if it's still active.
             val subscriptionModel = _subscriptionModelStore.get(createOperation.subscriptionId)
             subscriptionModel?.setStringProperty(
@@ -175,7 +188,13 @@ internal class SubscriptionOperationExecutor(
                     AndroidUtils.getAppVersion(_applicationService.appContext),
                 )
 
-            _subscriptionBackend.updateSubscription(lastOperation.appId, lastOperation.subscriptionId, subscription)
+            val rywToken = _subscriptionBackend.updateSubscription(lastOperation.appId, lastOperation.subscriptionId, subscription)
+
+            if (rywToken != null) {
+                _consistencyManager.setRywToken(startingOperation.onesignalId, IamFetchRywTokenKey.SUBSCRIPTION, rywToken)
+            } else {
+                _consistencyManager.resolveConditionsWithID(IamFetchReadyCondition.ID)
+            }
         } catch (ex: BackendException) {
             val responseType = NetworkUtils.getResponseStatusType(ex.statusCode)
 
@@ -216,6 +235,7 @@ internal class SubscriptionOperationExecutor(
         return ExecutionResponse(ExecutionResult.SUCCESS)
     }
 
+    // TODO: whenever the end-user changes users, we need to add the read-your-write token here, currently no code to handle the re-fetch IAMs
     private suspend fun transferSubscription(startingOperation: TransferSubscriptionOperation): ExecutionResponse {
         try {
             _subscriptionBackend.transferSubscription(
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt
index e3d2a7425f..0f28ae61d1 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/UpdateUserOperationExecutor.kt
@@ -1,6 +1,9 @@
 package com.onesignal.user.internal.operations.impl.executors
 
 import com.onesignal.common.NetworkUtils
+import com.onesignal.common.consistency.IamFetchReadyCondition
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.common.modeling.ModelChangeTags
 import com.onesignal.core.internal.operations.ExecutionResponse
@@ -31,12 +34,13 @@ internal class UpdateUserOperationExecutor(
     private val _propertiesModelStore: PropertiesModelStore,
     private val _buildUserService: IRebuildUserService,
     private val _newRecordState: NewRecordsState,
+    private val _consistencyManager: IConsistencyManager,
 ) : IOperationExecutor {
     override val operations: List<String>
         get() = listOf(SET_TAG, DELETE_TAG, SET_PROPERTY, TRACK_SESSION_START, TRACK_SESSION_END, TRACK_PURCHASE)
 
-    override suspend fun execute(ops: List<Operation>): ExecutionResponse {
-        Logging.log(LogLevel.DEBUG, "UpdateUserOperationExecutor(operation: $ops)")
+    override suspend fun execute(operations: List<Operation>): ExecutionResponse {
+        Logging.log(LogLevel.DEBUG, "UpdateUserOperationExecutor(operation: $operations)")
 
         var appId: String? = null
         var onesignalId: String? = null
@@ -45,7 +49,7 @@ internal class UpdateUserOperationExecutor(
         var deltasObject = PropertiesDeltasObject()
         var refreshDeviceMetadata = false
 
-        for (operation in ops) {
+        for (operation in operations) {
             when (operation) {
                 is SetTagOperation -> {
                     if (appId == null) {
@@ -83,7 +87,8 @@ internal class UpdateUserOperationExecutor(
                     // that exist in this group.
                     val sessionCount = if (deltasObject.sessionCount != null) deltasObject.sessionCount!! + 1 else 1
 
-                    deltasObject = PropertiesDeltasObject(deltasObject.sessionTime, sessionCount, deltasObject.amountSpent, deltasObject.purchases)
+                    deltasObject =
+                        PropertiesDeltasObject(deltasObject.sessionTime, sessionCount, deltasObject.amountSpent, deltasObject.purchases)
                     refreshDeviceMetadata = true
                 }
                 is TrackSessionEndOperation -> {
@@ -94,9 +99,15 @@ internal class UpdateUserOperationExecutor(
 
                     // The session time we pass up is the total session time across all `TrackSessionEndOperation`
                     // operations that exist in this group.
-                    val sessionTime = if (deltasObject.sessionTime != null) deltasObject.sessionTime!! + operation.sessionTime else operation.sessionTime
+                    val sessionTime =
+                        if (deltasObject.sessionTime != null) {
+                            deltasObject.sessionTime!! + operation.sessionTime
+                        } else {
+                            operation.sessionTime
+                        }
 
-                    deltasObject = PropertiesDeltasObject(sessionTime, deltasObject.sessionCount, deltasObject.amountSpent, deltasObject.purchases)
+                    deltasObject =
+                        PropertiesDeltasObject(sessionTime, deltasObject.sessionCount, deltasObject.amountSpent, deltasObject.purchases)
                 }
                 is TrackPurchaseOperation -> {
                     if (appId == null) {
@@ -107,7 +118,12 @@ internal class UpdateUserOperationExecutor(
                     // The amount spent we pass up is the total amount spent across all `TrackPurchaseOperation`
                     // operations that exist in this group, while the purchases is the union of all
                     // `TrackPurchaseOperation` operations that exist in this group.
-                    val amountSpent = if (deltasObject.amountSpent != null) deltasObject.amountSpent!! + operation.amountSpent else operation.amountSpent
+                    val amountSpent =
+                        if (deltasObject.amountSpent != null) {
+                            deltasObject.amountSpent!! + operation.amountSpent
+                        } else {
+                            operation.amountSpent
+                        }
                     val purchasesArray = if (deltasObject.purchases != null) deltasObject.purchases!!.toMutableList() else mutableListOf()
 
                     for (purchase in operation.purchases) {
@@ -122,18 +138,25 @@ internal class UpdateUserOperationExecutor(
 
         if (appId != null && onesignalId != null) {
             try {
-                _userBackend.updateUser(
-                    appId,
-                    IdentityConstants.ONESIGNAL_ID,
-                    onesignalId,
-                    propertiesObject,
-                    refreshDeviceMetadata,
-                    deltasObject,
-                )
+                val rywToken =
+                    _userBackend.updateUser(
+                        appId,
+                        IdentityConstants.ONESIGNAL_ID,
+                        onesignalId,
+                        propertiesObject,
+                        refreshDeviceMetadata,
+                        deltasObject,
+                    )
+
+                if (rywToken != null) {
+                    _consistencyManager.setRywToken(onesignalId, IamFetchRywTokenKey.USER, rywToken)
+                } else {
+                    _consistencyManager.resolveConditionsWithID(IamFetchReadyCondition.ID)
+                }
 
                 if (_identityModelStore.model.onesignalId == onesignalId) {
                     // go through and make sure any properties are in the correct model state
-                    for (operation in ops) {
+                    for (operation in operations) {
                         when (operation) {
                             is SetTagOperation ->
                                 _propertiesModelStore.model.tags.setStringProperty(
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/consistency/ConsistencyManagerTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/consistency/ConsistencyManagerTests.kt
new file mode 100644
index 0000000000..f92174e0b9
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/consistency/ConsistencyManagerTests.kt
@@ -0,0 +1,111 @@
+package com.onesignal.common.consistency.impl
+
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.ICondition
+import com.onesignal.common.consistency.models.IConsistencyKeyEnum
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+import kotlinx.coroutines.test.runTest
+
+class ConsistencyManagerTests : FunSpec({
+
+    lateinit var consistencyManager: ConsistencyManager
+
+    beforeAny {
+        consistencyManager = ConsistencyManager()
+    }
+
+    test("setRywToken updates the token correctly") {
+        runTest {
+            // Given
+            val id = "test_id"
+            val key = IamFetchRywTokenKey.USER
+            val value = "123"
+
+            consistencyManager.setRywToken(id, key, value)
+
+            val condition = TestMetCondition(mapOf(id to mapOf(key to value)))
+            val deferred = consistencyManager.registerCondition(condition)
+            val result = deferred.await()
+
+            result shouldBe value
+        }
+    }
+
+    test("registerCondition completes when condition is met") {
+        runTest {
+            // Given
+            val id = "test_id"
+            val key = IamFetchRywTokenKey.USER
+            val value = "123"
+
+            // Set a token to meet the condition
+            consistencyManager.setRywToken(id, key, value)
+
+            val condition = TestMetCondition(mapOf(id to mapOf(key to value)))
+            val deferred = consistencyManager.registerCondition(condition)
+
+            deferred.await()
+            deferred.isCompleted shouldBe true
+        }
+    }
+
+    test("registerCondition does not complete when condition is not met") {
+        runTest {
+            val condition = TestUnmetCondition()
+            val deferred = consistencyManager.registerCondition(condition)
+
+            consistencyManager.setRywToken("id", IamFetchRywTokenKey.USER, "123")
+            deferred.isCompleted shouldBe false
+        }
+    }
+
+    test("resolveConditionsWithID resolves conditions based on ID") {
+        runTest {
+            val condition = TestUnmetCondition()
+            val deferred = consistencyManager.registerCondition(condition)
+            consistencyManager.resolveConditionsWithID(TestUnmetCondition.ID)
+            deferred.await()
+
+            deferred.isCompleted shouldBe true
+        }
+    }
+}) {
+    // Mock implementation of ICondition that simulates a condition that isn't met
+    private class TestUnmetCondition : ICondition {
+        companion object {
+            const val ID = "TestUnmetCondition"
+        }
+
+        override val id: String
+            get() = ID
+
+        override fun isMet(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String>>): Boolean {
+            return false // Always returns false to simulate an unmet condition
+        }
+
+        override fun getNewestToken(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String?>>): String? {
+            return null
+        }
+    }
+
+    // Mock implementation of ICondition for cases where the condition is met
+    private class TestMetCondition(
+        private val expectedRywTokens: Map<String, Map<IConsistencyKeyEnum, String?>>,
+    ) : ICondition {
+        companion object {
+            const val ID = "TestMetCondition"
+        }
+
+        override val id: String
+            get() = ID
+
+        override fun isMet(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String>>): Boolean {
+            return indexedTokens == expectedRywTokens
+        }
+
+        override fun getNewestToken(indexedTokens: Map<String, Map<IConsistencyKeyEnum, String?>>): String? {
+            return expectedRywTokens.values.firstOrNull()?.values?.firstOrNull()
+        }
+    }
+}
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt
index 92cf55947d..c228a5c5cb 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/backend/SubscriptionBackendServiceTests.kt
@@ -42,7 +42,7 @@ class SubscriptionBackendServiceTests : FunSpec({
         val response = subscriptionBackendService.createSubscription("appId", aliasLabel, aliasValue, subscription)
 
         // Then
-        response shouldBe "subscriptionId"
+        response shouldBe Pair("subscriptionId", null)
         coVerify {
             spyHttpClient.post(
                 "apps/appId/users/by/$aliasLabel/$aliasValue/subscriptions",
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
index 37c290855e..87d5f4a0bd 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/SubscriptionOperationExecutorTests.kt
@@ -1,6 +1,8 @@
 package com.onesignal.user.internal.operations
 
 import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.core.internal.operations.ExecutionResult
 import com.onesignal.core.internal.operations.Operation
@@ -18,6 +20,7 @@ import com.onesignal.user.internal.subscriptions.SubscriptionStatus
 import com.onesignal.user.internal.subscriptions.SubscriptionType
 import io.kotest.core.spec.style.FunSpec
 import io.kotest.matchers.shouldBe
+import io.mockk.clearMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.every
@@ -27,664 +30,741 @@ import io.mockk.runs
 import io.mockk.verify
 
 @RobolectricTest
-class SubscriptionOperationExecutorTests : FunSpec({
-    val appId = "appId"
-    val remoteOneSignalId = "remote-onesignalId"
-    val localSubscriptionId = "local-subscriptionId1"
-    val remoteSubscriptionId = "remote-subscriptionId1"
-
-    test("create subscription successfully creates subscription") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } returns remoteSubscriptionId
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val subscriptionModel1 = SubscriptionModel()
-        subscriptionModel1.id = localSubscriptionId
-        every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
-
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
-                    appId,
-                    remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        subscriptionModel1.id shouldBe remoteSubscriptionId
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.createSubscription(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+class SubscriptionOperationExecutorTests :
+    FunSpec({
+        val appId = "appId"
+        val remoteOneSignalId = "remote-onesignalId"
+        val localSubscriptionId = "local-subscriptionId1"
+        val remoteSubscriptionId = "remote-subscriptionId1"
+        val rywToken = "1"
+        val mockConsistencyManager = mockk<IConsistencyManager>()
+
+        beforeTest {
+            clearMocks(mockConsistencyManager)
+            coEvery { mockConsistencyManager.setRywToken(any(), any(), any()) } just runs
         }
-    }
-
-    test("create subscription fails with retry when there is a network condition") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws BackendException(408, retryAfterSeconds = 10)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
+
+        test("create subscription successfully creates subscription") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } returns
+                Pair(remoteSubscriptionId, rywToken)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val subscriptionModel1 = SubscriptionModel()
+            subscriptionModel1.id = localSubscriptionId
+            every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            subscriptionModel1.id shouldBe remoteSubscriptionId
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.createSubscription(
                     appId,
+                    IdentityConstants.ONESIGNAL_ID,
                     remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-        response.retryAfterSeconds shouldBe 10
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.createSubscription(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("create subscription fails without retry when there is a backend error") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } answers { null }
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
+
+        test("create subscription fails with retry when there is a network condition") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws
+                BackendException(408, retryAfterSeconds = 10)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+            response.retryAfterSeconds shouldBe 10
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.createSubscription(
                     appId,
+                    IdentityConstants.ONESIGNAL_ID,
                     remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_NORETRY
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.createSubscription(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("create subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
-        val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                newRecordState,
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
-                    appId,
-                    remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-    }
-
-    test("create subscription then delete subscription is a successful no-op") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val subscriptionModel1 = SubscriptionModel()
-        subscriptionModel1.id = localSubscriptionId
-        every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
-
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
-                    appId,
-                    remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-                DeleteSubscriptionOperation(appId, remoteOneSignalId, localSubscriptionId),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-    }
-
-    test("create subscription then update subscription successfully creates subscription") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } returns remoteSubscriptionId
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val subscriptionModel1 = SubscriptionModel()
-        subscriptionModel1.id = localSubscriptionId
-        every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
-
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                CreateSubscriptionOperation(
-                    appId,
-                    remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken1",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-                UpdateSubscriptionOperation(
+
+        test("create subscription fails without retry when there is a backend error") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } answers { null }
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_NORETRY
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.createSubscription(
                     appId,
+                    IdentityConstants.ONESIGNAL_ID,
                     remoteOneSignalId,
-                    localSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken2",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        subscriptionModel1.id shouldBe remoteSubscriptionId
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.createSubscription(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken2"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("update subscription successfully updates subscription") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } just runs
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val subscriptionModel1 = SubscriptionModel()
-        subscriptionModel1.id = remoteSubscriptionId
-        subscriptionModel1.address = "pushToken1"
-        every { mockSubscriptionsModelStore.get(remoteSubscriptionId) } returns subscriptionModel1
-
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                UpdateSubscriptionOperation(
-                    appId,
-                    remoteOneSignalId,
-                    remoteSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken2",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-                UpdateSubscriptionOperation(
+
+        test("create subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
+            val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    newRecordState,
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+        }
+
+        test("create subscription then delete subscription is a successful no-op") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val subscriptionModel1 = SubscriptionModel()
+            subscriptionModel1.id = localSubscriptionId
+            every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                    DeleteSubscriptionOperation(appId, remoteOneSignalId, localSubscriptionId),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+        }
+
+        test("create subscription then update subscription successfully creates subscription") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.createSubscription(any(), any(), any(), any()) } returns
+                Pair(remoteSubscriptionId, rywToken)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val subscriptionModel1 = SubscriptionModel()
+            subscriptionModel1.id = localSubscriptionId
+            every { mockSubscriptionsModelStore.get(localSubscriptionId) } returns subscriptionModel1
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    CreateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken1",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        localSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            subscriptionModel1.id shouldBe remoteSubscriptionId
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.createSubscription(
                     appId,
+                    IdentityConstants.ONESIGNAL_ID,
                     remoteOneSignalId,
-                    remoteSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken3",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.updateSubscription(
-                appId,
-                remoteSubscriptionId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken3"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken2"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("update subscription fails with retry when there is a network condition") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(408)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                UpdateSubscriptionOperation(
+
+        test("update subscription successfully updates subscription") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } returns rywToken
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val subscriptionModel1 =
+                SubscriptionModel().apply {
+                    id = remoteSubscriptionId
+                    address = "pushToken1"
+                }
+            every { mockSubscriptionsModelStore.get(remoteSubscriptionId) } returns subscriptionModel1
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf(
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken3",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.updateSubscription(
                     appId,
-                    remoteOneSignalId,
                     remoteSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken2",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.updateSubscription(
-                appId,
-                remoteSubscriptionId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken2"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken3"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("update subscription fails without retry when there is a backend error") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                UpdateSubscriptionOperation(
+
+        test("update subscription fails with retry when there is a network condition") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(408)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.updateSubscription(
                     appId,
-                    remoteOneSignalId,
                     remoteSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken2",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_NORETRY
-        coVerify(exactly = 1) {
-            mockSubscriptionBackendService.updateSubscription(
-                appId,
-                remoteSubscriptionId,
-                withArg {
-                    it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
-                    it.enabled shouldBe true
-                    it.token shouldBe "pushToken2"
-                    it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
-                },
-            )
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken2"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
         }
-    }
-
-    test("update subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
-        val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                newRecordState,
-            )
-
-        val operations =
-            listOf<Operation>(
-                UpdateSubscriptionOperation(
+
+        test("update subscription fails without retry when there is a backend error") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_NORETRY
+            coVerify(exactly = 1) {
+                mockSubscriptionBackendService.updateSubscription(
                     appId,
-                    remoteOneSignalId,
                     remoteSubscriptionId,
-                    SubscriptionType.PUSH,
-                    true,
-                    "pushToken2",
-                    SubscriptionStatus.SUBSCRIBED,
-                ),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-    }
-
-    test("delete subscription successfully deletes subscription") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } just runs
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        every { mockSubscriptionsModelStore.remove(any(), any()) } just runs
-
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
-        verify(exactly = 1) { mockSubscriptionsModelStore.remove(remoteSubscriptionId, any()) }
-    }
-
-    test("delete subscription fails with retry when there is a network condition") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(408)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-        coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
-    }
-
-    // If we get a 404 then the subscription has already been deleted,
-    // so we count it as successful
-    test("delete subscription is successful if there is a 404") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-
-        val operations =
-            listOf<Operation>(
-                DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
-    }
-
-    test("delete subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
-        // Given
-        val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
-        coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(404)
-
-        val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
-        val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
-
-        val subscriptionOperationExecutor =
-            SubscriptionOperationExecutor(
-                mockSubscriptionBackendService,
-                MockHelper.deviceService(),
-                AndroidMockHelper.applicationService(),
-                mockSubscriptionsModelStore,
-                MockHelper.configModelStore(),
-                mockBuildUserService,
-                newRecordState,
-            )
-
-        val operations =
-            listOf<Operation>(
-                DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
-            )
-
-        // When
-        val response = subscriptionOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-    }
-})
+                    withArg {
+                        it.type shouldBe SubscriptionObjectType.ANDROID_PUSH
+                        it.enabled shouldBe true
+                        it.token shouldBe "pushToken2"
+                        it.notificationTypes shouldBe SubscriptionStatus.SUBSCRIBED.value
+                    },
+                )
+            }
+        }
+
+        test("update subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.updateSubscription(any(), any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
+            val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    newRecordState,
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+        }
+
+        test("delete subscription successfully deletes subscription") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } just runs
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            every { mockSubscriptionsModelStore.remove(any(), any()) } just runs
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
+            verify(exactly = 1) { mockSubscriptionsModelStore.remove(remoteSubscriptionId, any()) }
+        }
+
+        test("delete subscription fails with retry when there is a network condition") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(408)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+            coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
+        }
+
+        // If we get a 404 then the subscription has already been deleted,
+        // so we count it as successful
+        test("delete subscription is successful if there is a 404") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) { mockSubscriptionBackendService.deleteSubscription(appId, remoteSubscriptionId) }
+        }
+
+        test("delete subscription fails with retry when the backend returns MISSING, when isInMissingRetryWindow") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery { mockSubscriptionBackendService.deleteSubscription(any(), any()) } throws BackendException(404)
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
+            val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    newRecordState,
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    DeleteSubscriptionOperation(appId, remoteOneSignalId, remoteSubscriptionId),
+                )
+
+            // When
+            val response = subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+        }
+
+        test("setRywToken is called after successful subscription update") {
+            // Given
+            val mockSubscriptionBackendService = mockk<ISubscriptionBackendService>()
+            coEvery {
+                mockSubscriptionBackendService.updateSubscription(any(), any(), any())
+            } returns rywToken
+
+            val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
+            val subscriptionModel1 =
+                SubscriptionModel().apply {
+                    id = remoteSubscriptionId
+                    address = "pushToken1"
+                }
+            every { mockSubscriptionsModelStore.get(remoteSubscriptionId) } returns subscriptionModel1
+
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val subscriptionOperationExecutor =
+                SubscriptionOperationExecutor(
+                    mockSubscriptionBackendService,
+                    MockHelper.deviceService(),
+                    AndroidMockHelper.applicationService(),
+                    mockSubscriptionsModelStore,
+                    MockHelper.configModelStore(),
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf(
+                    UpdateSubscriptionOperation(
+                        appId,
+                        remoteOneSignalId,
+                        remoteSubscriptionId,
+                        SubscriptionType.PUSH,
+                        true,
+                        "pushToken2",
+                        SubscriptionStatus.SUBSCRIBED,
+                    ),
+                )
+
+            subscriptionOperationExecutor.execute(operations)
+
+            // Then
+            coVerify(exactly = 1) {
+                mockConsistencyManager.setRywToken(remoteOneSignalId, IamFetchRywTokenKey.SUBSCRIPTION, rywToken)
+            }
+        }
+    })
diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
index 529bb22a6f..4c511e3758 100644
--- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
+++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/user/internal/operations/UpdateUserOperationExecutorTests.kt
@@ -1,5 +1,7 @@
 package com.onesignal.user.internal.operations
 
+import com.onesignal.common.consistency.enums.IamFetchRywTokenKey
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.core.internal.operations.ExecutionResult
 import com.onesignal.core.internal.operations.Operation
@@ -13,6 +15,7 @@ import com.onesignal.user.internal.properties.PropertiesModel
 import io.kotest.core.spec.style.FunSpec
 import io.kotest.matchers.shouldBe
 import io.kotest.matchers.shouldNotBe
+import io.mockk.clearMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.every
@@ -21,322 +24,373 @@ import io.mockk.mockk
 import io.mockk.runs
 import java.math.BigDecimal
 
-class UpdateUserOperationExecutorTests : FunSpec({
-    val appId = "appId"
-    val localOneSignalId = "local-onesignalId"
-    val remoteOneSignalId = "remote-onesignalId"
-
-    test("update user single operation is successful") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } just runs
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations = listOf<Operation>(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockUserBackendService.updateUser(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.tags shouldBe mapOf("tagKey1" to "tagValue1")
-                },
-                any(),
-                any(),
-            )
+class UpdateUserOperationExecutorTests :
+    FunSpec({
+        val appId = "appId"
+        val localOneSignalId = "local-onesignalId"
+        val remoteOneSignalId = "remote-onesignalId"
+        val rywToken = "1"
+        val mockConsistencyManager = mockk<IConsistencyManager>()
+
+        beforeTest {
+            clearMocks(mockConsistencyManager)
+            coEvery { mockConsistencyManager.setRywToken(any(), any(), any()) } just runs
         }
-    }
-
-    test("update user multiple property operations are successful") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } just runs
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations =
-            listOf<Operation>(
-                SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-1"),
-                SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-2"),
-                SetTagOperation(appId, remoteOneSignalId, "tagKey2", "tagValue2"),
-                SetTagOperation(appId, remoteOneSignalId, "tagKey3", "tagValue3"),
-                DeleteTagOperation(appId, remoteOneSignalId, "tagKey3"),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang1"),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang2"),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::timezone.name, "timezone"),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::country.name, "country"),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLatitude.name, 123.45),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLongitude.name, 678.90),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationType.name, 1),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationAccuracy.name, 0.15),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationBackground.name, true),
-                SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationTimestamp.name, 1111L),
-            )
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockUserBackendService.updateUser(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.tags shouldBe mapOf("tagKey1" to "tagValue1-2", "tagKey2" to "tagValue2", "tagKey3" to null)
-                    it.country shouldBe "country"
-                    it.language shouldBe "lang2"
-                    it.timezoneId shouldBe "timezone"
-                    it.latitude shouldBe 123.45
-                    it.longitude shouldBe 678.90
-                },
-                any(),
-                any(),
-            )
+
+        test("update user single operation is successful") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } returns rywToken
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations = listOf<Operation>(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockUserBackendService.updateUser(
+                    appId,
+                    IdentityConstants.ONESIGNAL_ID,
+                    remoteOneSignalId,
+                    withArg {
+                        it.tags shouldBe mapOf("tagKey1" to "tagValue1")
+                    },
+                    any(),
+                    any(),
+                )
+            }
         }
-    }
-
-    test("update user single property delta operations is successful") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } just runs
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations =
-            listOf<Operation>(
-                TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
-            )
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockUserBackendService.updateUser(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.tags shouldBe null
-                },
-                any(),
-                withArg {
-                    it.sessionTime shouldBe 1111
-                },
-            )
+
+        test("update user multiple property operations are successful") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } returns rywToken
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations =
+                listOf<Operation>(
+                    SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-1"),
+                    SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1-2"),
+                    SetTagOperation(appId, remoteOneSignalId, "tagKey2", "tagValue2"),
+                    SetTagOperation(appId, remoteOneSignalId, "tagKey3", "tagValue3"),
+                    DeleteTagOperation(appId, remoteOneSignalId, "tagKey3"),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang1"),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::language.name, "lang2"),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::timezone.name, "timezone"),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::country.name, "country"),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLatitude.name, 123.45),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationLongitude.name, 678.90),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationType.name, 1),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationAccuracy.name, 0.15),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationBackground.name, true),
+                    SetPropertyOperation(appId, localOneSignalId, PropertiesModel::locationTimestamp.name, 1111L),
+                )
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockUserBackendService.updateUser(
+                    appId,
+                    IdentityConstants.ONESIGNAL_ID,
+                    remoteOneSignalId,
+                    withArg {
+                        it.tags shouldBe mapOf("tagKey1" to "tagValue1-2", "tagKey2" to "tagValue2", "tagKey3" to null)
+                        it.country shouldBe "country"
+                        it.language shouldBe "lang2"
+                        it.timezoneId shouldBe "timezone"
+                        it.latitude shouldBe 123.45
+                        it.longitude shouldBe 678.90
+                    },
+                    any(),
+                    any(),
+                )
+            }
         }
-    }
-
-    test("update user multiple property delta operations are successful") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } just runs
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations =
-            listOf<Operation>(
-                TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
-                TrackPurchaseOperation(
+
+        test("update user single property delta operations is successful") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } returns rywToken
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations =
+                listOf<Operation>(
+                    TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
+                )
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockUserBackendService.updateUser(
                     appId,
+                    IdentityConstants.ONESIGNAL_ID,
                     remoteOneSignalId,
-                    false,
-                    BigDecimal(2222),
-                    listOf(
-                        PurchaseInfo("sku1", "iso1", BigDecimal(1000)),
-                        PurchaseInfo("sku2", "iso2", BigDecimal(1222)),
+                    withArg {
+                        it.tags shouldBe null
+                    },
+                    any(),
+                    withArg {
+                        it.sessionTime shouldBe 1111
+                    },
+                )
+            }
+        }
+
+        test("update user multiple property delta operations are successful") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } returns rywToken
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations =
+                listOf<Operation>(
+                    TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
+                    TrackPurchaseOperation(
+                        appId,
+                        remoteOneSignalId,
+                        false,
+                        BigDecimal(2222),
+                        listOf(
+                            PurchaseInfo("sku1", "iso1", BigDecimal(1000)),
+                            PurchaseInfo("sku2", "iso2", BigDecimal(1222)),
+                        ),
                     ),
-                ),
-                TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
-            )
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockUserBackendService.updateUser(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.tags shouldBe null
-                },
-                any(),
-                withArg {
-                    it.sessionTime shouldBe (1111 + 3333)
-                    it.amountSpent shouldBe BigDecimal(2222)
-                    it.purchases shouldNotBe null
-                    it.purchases!!.count() shouldBe 2
-                    it.purchases!![0].sku shouldBe "sku1"
-                    it.purchases!![0].iso shouldBe "iso1"
-                    it.purchases!![0].amount shouldBe BigDecimal(1000)
-                    it.purchases!![1].sku shouldBe "sku2"
-                    it.purchases!![1].iso shouldBe "iso2"
-                    it.purchases!![1].amount shouldBe BigDecimal(1222)
-                },
-            )
+                    TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
+                )
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockUserBackendService.updateUser(
+                    appId,
+                    IdentityConstants.ONESIGNAL_ID,
+                    remoteOneSignalId,
+                    withArg {
+                        it.tags shouldBe null
+                    },
+                    any(),
+                    withArg {
+                        it.sessionTime shouldBe (1111 + 3333)
+                        it.amountSpent shouldBe BigDecimal(2222)
+                        it.purchases shouldNotBe null
+                        it.purchases!!.count() shouldBe 2
+                        it.purchases!![0].sku shouldBe "sku1"
+                        it.purchases!![0].iso shouldBe "iso1"
+                        it.purchases!![0].amount shouldBe BigDecimal(1000)
+                        it.purchases!![1].sku shouldBe "sku2"
+                        it.purchases!![1].iso shouldBe "iso2"
+                        it.purchases!![1].amount shouldBe BigDecimal(1222)
+                    },
+                )
+            }
+        }
+
+        test("update user with both property and property delta operations are successful") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } returns rywToken
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations =
+                listOf<Operation>(
+                    TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
+                    SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"),
+                    TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
+                )
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.SUCCESS
+            coVerify(exactly = 1) {
+                mockUserBackendService.updateUser(
+                    appId,
+                    IdentityConstants.ONESIGNAL_ID,
+                    remoteOneSignalId,
+                    withArg {
+                        it.tags shouldBe mapOf("tagKey1" to "tagValue1")
+                    },
+                    any(),
+                    withArg {
+                        it.sessionTime shouldBe (1111 + 3333)
+                    },
+                )
+            }
         }
-    }
-
-    test("update user with both property and property delta operations are successful") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } just runs
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations =
-            listOf<Operation>(
-                TrackSessionEndOperation(appId, remoteOneSignalId, 1111),
-                SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"),
-                TrackSessionEndOperation(appId, remoteOneSignalId, 3333),
-            )
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.SUCCESS
-        coVerify(exactly = 1) {
-            mockUserBackendService.updateUser(
-                appId,
-                IdentityConstants.ONESIGNAL_ID,
-                remoteOneSignalId,
-                withArg {
-                    it.tags shouldBe mapOf("tagKey1" to "tagValue1")
-                },
-                any(),
-                withArg {
-                    it.sessionTime shouldBe (1111 + 3333)
-                },
-            )
+
+        test("update user single operation fails with MISSING") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } throws BackendException(404)
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } returns null
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+            val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_NORETRY
+        }
+
+        test("update user single operation fails with MISSING, but isInMissingRetryWindow") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } throws
+                BackendException(404, retryAfterSeconds = 10)
+
+            // Given
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+            every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } returns null
+
+            val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
+            val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    newRecordState,
+                    mockConsistencyManager,
+                )
+            val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
+
+            // When
+            val response = loginUserOperationExecutor.execute(operations)
+
+            // Then
+            response.result shouldBe ExecutionResult.FAIL_RETRY
+            response.retryAfterSeconds shouldBe 10
+        }
+
+        test("setRywToken is called after successful user update of session count") {
+            // Given
+            val mockUserBackendService = mockk<IUserBackendService>()
+            coEvery {
+                mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any())
+            } returns rywToken
+
+            val mockIdentityModelStore = MockHelper.identityModelStore()
+            val mockPropertiesModelStore = MockHelper.propertiesModelStore()
+            val mockBuildUserService = mockk<IRebuildUserService>()
+
+            val loginUserOperationExecutor =
+                UpdateUserOperationExecutor(
+                    mockUserBackendService,
+                    mockIdentityModelStore,
+                    mockPropertiesModelStore,
+                    mockBuildUserService,
+                    getNewRecordState(),
+                    mockConsistencyManager,
+                )
+
+            val operations =
+                listOf<Operation>(
+                    TrackSessionStartOperation(appId, onesignalId = remoteOneSignalId),
+                )
+
+            // When
+            loginUserOperationExecutor.execute(operations)
+
+            // Then
+            coVerify(exactly = 1) {
+                mockConsistencyManager.setRywToken(remoteOneSignalId, IamFetchRywTokenKey.USER, rywToken)
+            }
         }
-    }
-
-    test("update user single operation fails with MISSING") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } throws BackendException(404)
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } returns null
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                getNewRecordState(),
-            )
-        val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_NORETRY
-    }
-
-    test("update user single operation fails with MISSING, but isInMissingRetryWindow") {
-        // Given
-        val mockUserBackendService = mockk<IUserBackendService>()
-        coEvery { mockUserBackendService.updateUser(any(), any(), any(), any(), any(), any()) } throws BackendException(404, retryAfterSeconds = 10)
-
-        // Given
-        val mockIdentityModelStore = MockHelper.identityModelStore()
-        val mockPropertiesModelStore = MockHelper.propertiesModelStore()
-        val mockBuildUserService = mockk<IRebuildUserService>()
-        every { mockBuildUserService.getRebuildOperationsIfCurrentUser(any(), any()) } returns null
-
-        val mockConfigModelStore = MockHelper.configModelStore().also { it.model.opRepoPostCreateRetryUpTo = 1_000 }
-        val newRecordState = getNewRecordState(mockConfigModelStore).also { it.add(remoteOneSignalId) }
-
-        val loginUserOperationExecutor =
-            UpdateUserOperationExecutor(
-                mockUserBackendService,
-                mockIdentityModelStore,
-                mockPropertiesModelStore,
-                mockBuildUserService,
-                newRecordState,
-            )
-        val operations = listOf(SetTagOperation(appId, remoteOneSignalId, "tagKey1", "tagValue1"))
-
-        // When
-        val response = loginUserOperationExecutor.execute(operations)
-
-        // Then
-        response.result shouldBe ExecutionResult.FAIL_RETRY
-        response.retryAfterSeconds shouldBe 10
-    }
-})
+    })
diff --git a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/InAppMessagesManager.kt b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/InAppMessagesManager.kt
index 4c3f1e5d88..80b4071daa 100644
--- a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/InAppMessagesManager.kt
+++ b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/InAppMessagesManager.kt
@@ -4,6 +4,8 @@ import android.app.AlertDialog
 import com.onesignal.common.AndroidUtils
 import com.onesignal.common.IDManager
 import com.onesignal.common.JSONUtils
+import com.onesignal.common.consistency.IamFetchReadyCondition
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.common.events.EventProducer
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.common.modeling.ISingletonModelStoreChangeHandler
@@ -66,6 +68,7 @@ internal class InAppMessagesManager(
     private val _lifecycle: IInAppLifecycleService,
     private val _languageContext: ILanguageContext,
     private val _time: ITime,
+    private val _consistencyManager: IConsistencyManager,
 ) : IInAppMessagesManager,
     IStartableService,
     ISubscriptionChangedHandler,
@@ -149,7 +152,13 @@ internal class InAppMessagesManager(
             }
 
             // attempt to fetch messages from the backend (if we have the pre-requisite data already)
-            fetchMessages()
+            val onesignalId = _userManager.onesignalId
+            val updateConditionDeferred =
+                _consistencyManager.registerCondition(IamFetchReadyCondition(onesignalId))
+            val rywToken = updateConditionDeferred.await()
+            if (rywToken != null) {
+                fetchMessages(rywToken)
+            }
         }
     }
 
@@ -181,18 +190,14 @@ internal class InAppMessagesManager(
             return
         }
 
-        suspendifyOnThread {
-            fetchMessages()
-        }
+        fetchMessagesWhenConditionIsMet()
     }
 
     override fun onModelReplaced(
         model: ConfigModel,
         tag: String,
     ) {
-        suspendifyOnThread {
-            fetchMessages()
-        }
+        fetchMessagesWhenConditionIsMet()
     }
 
     override fun onSubscriptionAdded(subscription: ISubscription) { }
@@ -207,9 +212,7 @@ internal class InAppMessagesManager(
             return
         }
 
-        suspendifyOnThread {
-            fetchMessages()
-        }
+        fetchMessagesWhenConditionIsMet()
     }
 
     override fun onSessionStarted() {
@@ -217,17 +220,28 @@ internal class InAppMessagesManager(
             redisplayInAppMessage.isDisplayedInSession = false
         }
 
-        suspendifyOnThread {
-            fetchMessages()
-        }
+        fetchMessagesWhenConditionIsMet()
     }
 
     override fun onSessionActive() { }
 
     override fun onSessionEnded(duration: Long) { }
 
+    private fun fetchMessagesWhenConditionIsMet() {
+        suspendifyOnThread {
+            val onesignalId = _userManager.onesignalId
+            val iamFetchCondition =
+                _consistencyManager.registerCondition(IamFetchReadyCondition(onesignalId))
+            val rywToken = iamFetchCondition.await()
+
+            if (rywToken != null) {
+                fetchMessages(rywToken)
+            }
+        }
+    }
+
     // called when a new push subscription is added, or the app id is updated, or a new session starts
-    private suspend fun fetchMessages() {
+    private suspend fun fetchMessages(rywToken: String?) {
         // We only want to fetch IAMs if we know the app is in the
         // foreground, as we don't want to do this for background
         // events (such as push received), wasting resources for
@@ -252,7 +266,9 @@ internal class InAppMessagesManager(
             lastTimeFetchedIAMs = now
         }
 
-        val newMessages = _backend.listInAppMessages(appId, subscriptionId)
+        // lambda so that it is updated on each potential retry
+        val sessionDurationProvider = { _time.currentTimeMillis - _sessionService.startTime }
+        val newMessages = _backend.listInAppMessages(appId, subscriptionId, rywToken, sessionDurationProvider)
 
         if (newMessages != null) {
             this.messages = newMessages as MutableList<InAppMessage>
@@ -517,7 +533,9 @@ internal class InAppMessagesManager(
         if (triggerModel != null) {
             triggerModel.value = value
         } else {
-            triggerModel = com.onesignal.inAppMessages.internal.triggers.TriggerModel()
+            triggerModel =
+                com.onesignal.inAppMessages.internal.triggers
+                    .TriggerModel()
             triggerModel.id = key
             triggerModel.key = key
             triggerModel.value = value
@@ -782,13 +800,15 @@ internal class InAppMessagesManager(
     private fun logInAppMessagePreviewActions(action: InAppMessageClickResult) {
         if (action.tags != null) {
             Logging.debug(
-                "InAppMessagesManager.logInAppMessagePreviewActions: Tags detected inside of the action click payload, ignoring because action came from IAM preview:: " + action.tags.toString(),
+                "InAppMessagesManager.logInAppMessagePreviewActions: Tags detected inside of the action click payload, ignoring because action came from IAM preview:: " +
+                    action.tags.toString(),
             )
         }
 
         if (action.outcomes.size > 0) {
             Logging.debug(
-                "InAppMessagesManager.logInAppMessagePreviewActions: Outcomes detected inside of the action click payload, ignoring because action came from IAM preview: " + action.outcomes.toString(),
+                "InAppMessagesManager.logInAppMessagePreviewActions: Outcomes detected inside of the action click payload, ignoring because action came from IAM preview: " +
+                    action.outcomes.toString(),
             )
         }
 
@@ -890,7 +910,8 @@ internal class InAppMessagesManager(
     ) {
         val messageTitle = _applicationService.appContext.getString(R.string.location_permission_missing_title)
         val message = _applicationService.appContext.getString(R.string.location_permission_missing_message)
-        AlertDialog.Builder(_applicationService.current)
+        AlertDialog
+            .Builder(_applicationService.current)
             .setTitle(messageTitle)
             .setMessage(message)
             .setPositiveButton(android.R.string.ok) { _, _ -> suspendifyOnThread { showMultiplePrompts(inAppMessage, prompts) } }
diff --git a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/IInAppBackendService.kt b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/IInAppBackendService.kt
index adafdce86f..85db9f6896 100644
--- a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/IInAppBackendService.kt
+++ b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/IInAppBackendService.kt
@@ -13,12 +13,16 @@ internal interface IInAppBackendService {
      *
      * @param appId The ID of the application that the IAM will be retrieved from.
      * @param subscriptionId The specific subscription within the [appId] the IAM will be delivered to.
+     * @param rywToken Used for read your write consistency
+     * @param sessionDurationProvider Lambda to calculate the session duration at the time of the request
      *
      * @return The list of IAMs associated to the subscription, or null if the IAMs could not be retrieved.
      */
     suspend fun listInAppMessages(
         appId: String,
         subscriptionId: String,
+        rywToken: String?,
+        sessionDurationProvider: () -> Long,
     ): List<InAppMessage>?
 
     /**
diff --git a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/impl/InAppBackendService.kt b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/impl/InAppBackendService.kt
index c318f97f97..f541f9b321 100644
--- a/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/impl/InAppBackendService.kt
+++ b/OneSignalSDK/onesignal/in-app-messages/src/main/java/com/onesignal/inAppMessages/internal/backend/impl/InAppBackendService.kt
@@ -4,12 +4,14 @@ import com.onesignal.common.NetworkUtils
 import com.onesignal.common.exceptions.BackendException
 import com.onesignal.core.internal.device.IDeviceService
 import com.onesignal.core.internal.http.IHttpClient
+import com.onesignal.core.internal.http.impl.OptionalHeaders
 import com.onesignal.debug.internal.logging.Logging
 import com.onesignal.inAppMessages.internal.InAppMessage
 import com.onesignal.inAppMessages.internal.InAppMessageContent
 import com.onesignal.inAppMessages.internal.backend.GetIAMDataResponse
 import com.onesignal.inAppMessages.internal.backend.IInAppBackendService
 import com.onesignal.inAppMessages.internal.hydrators.InAppHydrator
+import kotlinx.coroutines.delay
 import org.json.JSONObject
 
 internal class InAppBackendService(
@@ -22,24 +24,11 @@ internal class InAppBackendService(
     override suspend fun listInAppMessages(
         appId: String,
         subscriptionId: String,
+        rywToken: String?,
+        sessionDurationProvider: () -> Long,
     ): List<InAppMessage>? {
-        // Retrieve any in app messages that might exist
-        val response = _httpClient.get("apps/$appId/subscriptions/$subscriptionId/iams")
-
-        if (response.isSuccess) {
-            val jsonResponse = JSONObject(response.payload)
-
-            if (jsonResponse.has("in_app_messages")) {
-                val iamMessagesAsJSON = jsonResponse.getJSONArray("in_app_messages")
-                // TODO: Outstanding question on whether we still want to cache this.  Only used when
-                //       hard start of the app, but within 30 seconds of it being killed (i.e. same session startup).
-                // Cache copy for quick cold starts
-//                _prefs.savedIAMs = iamMessagesAsJSON.toString()
-                return _hydrator.hydrateIAMMessages(iamMessagesAsJSON)
-            }
-        }
-
-        return null
+        val baseUrl = "apps/$appId/subscriptions/$subscriptionId/iams"
+        return attemptFetchWithRetries(baseUrl, rywToken, sessionDurationProvider)
     }
 
     override suspend fun getIAMData(
@@ -51,7 +40,7 @@ internal class InAppBackendService(
             htmlPathForMessage(messageId, variantId, appId)
                 ?: return GetIAMDataResponse(null, false)
 
-        val response = _httpClient.get(htmlPath, null)
+        val response = _httpClient.get(htmlPath)
 
         if (response.isSuccess) {
             // Successful request, reset count
@@ -81,7 +70,7 @@ internal class InAppBackendService(
     ): InAppMessageContent? {
         val htmlPath = "in_app_messages/device_preview?preview_id=$previewUUID&app_id=$appId"
 
-        val response = _httpClient.get(htmlPath, null)
+        val response = _httpClient.get(htmlPath)
 
         return if (response.isSuccess) {
             val jsonResponse = JSONObject(response.payload!!)
@@ -209,4 +198,74 @@ internal class InAppBackendService(
     ) {
         Logging.error("Encountered a $statusCode error while attempting in-app message $requestType request: $response")
     }
+
+    private suspend fun attemptFetchWithRetries(
+        baseUrl: String,
+        rywToken: String?,
+        sessionDurationProvider: () -> Long,
+    ): List<InAppMessage>? {
+        var attempts = 0
+        var retryLimit: Int = 0 // retry limit is remote defined & set dynamically below
+
+        do {
+            val retryCount = if (attempts > 0) attempts else null
+            val values =
+                OptionalHeaders(
+                    rywToken = rywToken,
+                    sessionDuration = sessionDurationProvider(),
+                    retryCount = retryCount,
+                )
+            val response = _httpClient.get(baseUrl, values)
+
+            if (response.isSuccess) {
+                val jsonResponse = response.payload?.let { JSONObject(it) }
+                return jsonResponse?.let { hydrateInAppMessages(it) }
+            } else if (response.statusCode == 425 || response.statusCode == 429) {
+                // update the retry limit from response
+                retryLimit = response.retryLimit ?: retryLimit
+
+                // apply the Retry-After delay if present
+                response.retryAfterSeconds?.let {
+                    delay(it * 1_000L)
+                }
+            } else if (response.statusCode in 500..599) {
+                return null
+            } else {
+                return null
+            }
+
+            attempts++
+        } while (attempts <= retryLimit)
+
+        // Final attempt without the RYW token if retries fail
+        return fetchInAppMessagesWithoutRywToken(baseUrl, sessionDurationProvider)
+    }
+
+    private suspend fun fetchInAppMessagesWithoutRywToken(
+        url: String,
+        sessionDurationProvider: () -> Long,
+    ): List<InAppMessage>? {
+        val response =
+            _httpClient.get(
+                url,
+                OptionalHeaders(
+                    sessionDuration = sessionDurationProvider(),
+                ),
+            )
+
+        if (response.isSuccess) {
+            val jsonResponse = response.payload?.let { JSONObject(it) }
+            return jsonResponse?.let { hydrateInAppMessages(it) }
+        } else {
+            return null
+        }
+    }
+
+    private fun hydrateInAppMessages(jsonResponse: JSONObject): List<InAppMessage>? =
+        if (jsonResponse.has("in_app_messages")) {
+            val iamMessagesAsJSON = jsonResponse.getJSONArray("in_app_messages")
+            _hydrator.hydrateIAMMessages(iamMessagesAsJSON)
+        } else {
+            null
+        }
 }
diff --git a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/InAppMessagesManagerTests.kt b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/InAppMessagesManagerTests.kt
index fa5d4c16ed..f2753bcff8 100644
--- a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/InAppMessagesManagerTests.kt
+++ b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/InAppMessagesManagerTests.kt
@@ -1,5 +1,6 @@
 package com.onesignal.inAppMessages.internal
 
+import com.onesignal.common.consistency.models.IConsistencyManager
 import com.onesignal.core.internal.config.ConfigModelStore
 import com.onesignal.inAppMessages.internal.backend.IInAppBackendService
 import com.onesignal.inAppMessages.internal.display.IInAppDisplayer
@@ -52,6 +53,7 @@ class InAppMessagesManagerTests : FunSpec({
                 mockk<IInAppLifecycleService>(),
                 MockHelper.languageContext(),
                 MockHelper.time(1000),
+                mockk<IConsistencyManager>(),
             )
 
         // When
diff --git a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt
index 8fa82a50c6..379013d7c6 100644
--- a/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt
+++ b/OneSignalSDK/onesignal/in-app-messages/src/test/java/com/onesignal/inAppMessages/internal/backend/InAppBackendServiceTests.kt
@@ -22,435 +22,508 @@ import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.mockk
 
-class InAppBackendServiceTests : FunSpec({
-    beforeAny {
-        Logging.logLevel = LogLevel.NONE
-    }
-
-    test("listInAppMessages with no messages returns zero-lengthed array") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{ in_app_messages: [] }")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.listInAppMessages("appId", "subscriptionId")
-
-        // Then
-        response shouldNotBe null
-        response!!.count() shouldBe 0
-        coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
-    }
-
-    test("listInAppMessages with 1 message returns one-lengthed array") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery {
-            mockHttpClient.get(any(), any())
-        } returns HttpResponse(200, "{ in_app_messages: [{id: \"messageId1\", variants:{all: {en: \"content1\"}}, triggers:[[{id: \"triggerId1\", kind: \"custom\", property: \"property1\", operator: \"equal\", value: \"value1\"}]], end_time: \"2008-09-03T20:56:35.450686Z\", redisplay: { limit: 11111, delay: 22222}}] }")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.listInAppMessages("appId", "subscriptionId")
-
-        // Then
-        response shouldNotBe null
-        response!!.count() shouldBe 1
-        response[0].messageId shouldBe "messageId1"
-        response[0].variants.keys.count() shouldBe 1
-        response[0].variants["all"] shouldNotBe null
-        response[0].variants["all"]!!.keys.count() shouldBe 1
-        response[0].variants["all"]!!["en"] shouldBe "content1"
-        response[0].triggers.count() shouldBe 1
-        response[0].triggers[0].count() shouldBe 1
-        response[0].triggers[0][0].triggerId shouldBe "triggerId1"
-        response[0].triggers[0][0].kind shouldBe Trigger.OSTriggerKind.CUSTOM
-        response[0].triggers[0][0].property shouldBe "property1"
-        response[0].triggers[0][0].operatorType shouldBe Trigger.OSTriggerOperator.EQUAL_TO
-        response[0].triggers[0][0].value shouldBe "value1"
-        response[0].isFinished shouldBe true
-        response[0].redisplayStats.displayLimit shouldBe 11111
-        response[0].redisplayStats.displayDelay shouldBe 22222
-
-        coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
-    }
-
-    test("listInAppMessages returns null when non-success response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.listInAppMessages("appId", "subscriptionId")
-
-        // Then
-        response shouldBe null
-        coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
-    }
-
-    test("getIAMData successfully hydrates successful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery {
-            mockHttpClient.get(any(), any())
-        } returns HttpResponse(200, "{html: \"html1\", display_duration: 123, styles: {remove_height_margin: true, remove_width_margin: true}}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response shouldNotBe null
-        response.shouldRetry shouldBe false
-        response.content shouldNotBe null
-        response.content!!.contentHtml shouldStartWith "html1"
-        response.content!!.displayDuration shouldBe 123
-        response.content!!.useHeightMargin shouldBe false
-        response.content!!.useWidthMargin shouldBe false
-        response.content!!.isFullBleed shouldBe true
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMData successfully hydrates successful response with no content") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response shouldNotBe null
-        response.shouldRetry shouldBe false
-        response.content shouldBe null
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMData successfully hydrates successful response with no style") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{html: \"html1\", display_duration: 123 }")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response shouldNotBe null
-        response.shouldRetry shouldBe false
-        response.content shouldNotBe null
-        response.content!!.contentHtml shouldStartWith "html1"
-        response.content!!.displayDuration shouldBe 123
-        response.content!!.useHeightMargin shouldBe true
-        response.content!!.useWidthMargin shouldBe true
-        response.content!!.isFullBleed shouldBe false
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMData indicates retry when retryable response provided") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(500, null)
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response shouldNotBe null
-        response.shouldRetry shouldBe true
-        response.content shouldBe null
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMData indicates no retry when non-retryable response provided") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response shouldNotBe null
-        response.shouldRetry shouldBe false
-        response.content shouldBe null
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMData indicates no retry when retryable response provided more than 3 times") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(500, null)
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response1 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-        val response2 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-        val response3 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-        val response4 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
-
-        // Then
-        response1 shouldNotBe null
-        response1.shouldRetry shouldBe true
-        response1.content shouldBe null
-        response2 shouldNotBe null
-        response2.shouldRetry shouldBe true
-        response2.content shouldBe null
-        response3 shouldNotBe null
-        response3.shouldRetry shouldBe true
-        response3.content shouldBe null
-        response4 shouldNotBe null
-        response4.shouldRetry shouldBe false
-        response4.content shouldBe null
-
-        coVerify(exactly = 4) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
-    }
-
-    test("getIAMPreviewData successfully hydrates successful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery {
-            mockHttpClient.get(any(), any())
-        } returns HttpResponse(200, "{html: \"html1\", display_duration: 123, styles: {remove_height_margin: true, remove_width_margin: true}}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMPreviewData("appId", "previewUUID")
-
-        // Then
-        response shouldNotBe null
-        response!!.contentHtml shouldStartWith "html1"
-        response!!.displayDuration shouldBe 123
-        response!!.useHeightMargin shouldBe false
-        response!!.useWidthMargin shouldBe false
-        response!!.isFullBleed shouldBe true
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/device_preview?preview_id=previewUUID&app_id=appId", any()) }
-    }
-
-    test("getIAMPreviewData returns no data when response is unsuccessful") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val response = inAppBackendService.getIAMPreviewData("appId", "previewUUID")
-
-        // Then
-        response shouldBe null
-
-        coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/device_preview?preview_id=previewUUID&app_id=appId", any()) }
-    }
-
-    test("sendIAMClick is successful when there is a successful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        inAppBackendService.sendIAMClick("appId", "subscriptionId", "variantId", "messageId", "clickId", isFirstClick = true)
-
-        // Then
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/click",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("click_id") shouldBe "clickId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeBool("first_click") shouldBe true
-                },
-            )
+class InAppBackendServiceTests :
+    FunSpec({
+        val mockSessionDurationProvider: () -> Long = { 456L } // Use any fixed value here
+
+        beforeAny {
+            Logging.logLevel = LogLevel.NONE
+        }
+
+        test("listInAppMessages with no messages returns zero-lengthed array") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{ in_app_messages: [] }")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.listInAppMessages("appId", "subscriptionId", "123", mockSessionDurationProvider)
+
+            // Then
+            response shouldNotBe null
+            response!!.count() shouldBe 0
+            coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
         }
-    }
-
-    test("sendIAMClick throws exception when there is an unsuccessful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        val exception =
-            shouldThrowUnit<BackendException> {
-                inAppBackendService.sendIAMClick(
-                    "appId",
-                    "subscriptionId",
-                    "variantId",
-                    "messageId",
-                    "clickId",
-                    isFirstClick = true,
+
+        test("listInAppMessages with 1 message returns one-lengthed array") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery {
+                mockHttpClient.get(any(), any())
+            } returns
+                HttpResponse(
+                    200,
+                    "{ in_app_messages: [{id: \"messageId1\", variants:{all: {en: \"content1\"}}, triggers:[[{id: \"triggerId1\", kind: \"custom\", property: \"property1\", operator: \"equal\", value: \"value1\"}]], end_time: \"2008-09-03T20:56:35.450686Z\", redisplay: { limit: 11111, delay: 22222}}] }",
                 )
-            }
 
-        // Then
-        exception.statusCode shouldBe 409
-        exception.response shouldBe "{}"
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/click",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("click_id") shouldBe "clickId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeBool("first_click") shouldBe true
-                },
-            )
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.listInAppMessages("appId", "subscriptionId", "123", mockSessionDurationProvider)
+
+            // Then
+            response shouldNotBe null
+            response!!.count() shouldBe 1
+            response[0].messageId shouldBe "messageId1"
+            response[0].variants.keys.count() shouldBe 1
+            response[0].variants["all"] shouldNotBe null
+            response[0].variants["all"]!!.keys.count() shouldBe 1
+            response[0].variants["all"]!!["en"] shouldBe "content1"
+            response[0].triggers.count() shouldBe 1
+            response[0].triggers[0].count() shouldBe 1
+            response[0].triggers[0][0].triggerId shouldBe "triggerId1"
+            response[0].triggers[0][0].kind shouldBe Trigger.OSTriggerKind.CUSTOM
+            response[0].triggers[0][0].property shouldBe "property1"
+            response[0].triggers[0][0].operatorType shouldBe Trigger.OSTriggerOperator.EQUAL_TO
+            response[0].triggers[0][0].value shouldBe "value1"
+            response[0].isFinished shouldBe true
+            response[0].redisplayStats.displayLimit shouldBe 11111
+            response[0].redisplayStats.displayDelay shouldBe 22222
+
+            coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
         }
-    }
-
-    test("sendIAMImpression is successful when there is a successful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        inAppBackendService.sendIAMImpression("appId", "subscriptionId", "variantId", "messageId")
-
-        // Then
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/impression",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeBool("first_impression") shouldBe true
-                },
-            )
+
+        test("listInAppMessages returns null when non-success response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.listInAppMessages("appId", "subscriptionId", "123", mockSessionDurationProvider)
+
+            // Then
+            response shouldBe null
+            coVerify(exactly = 1) { mockHttpClient.get("apps/appId/subscriptions/subscriptionId/iams", any()) }
         }
-    }
 
-    test("sendIAMImpression throws exception when there is an unsuccessful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
+        test(
+            "retries according to retry limit and retryAfterSeconds and makes final request without RYW token after retries exhausted",
+        ) {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+
+            // Mock the first three attempts to return a 429 Too Many Requests response with retry limits
+            coEvery {
+                mockHttpClient.get(any(), any())
+            } returnsMany
+                listOf(
+                    HttpResponse(425, null, retryAfterSeconds = 1, retryLimit = 3),
+                    HttpResponse(425, null, retryAfterSeconds = 1, retryLimit = 3),
+                    HttpResponse(425, null, retryAfterSeconds = 1, retryLimit = 3),
+                    HttpResponse(425, null, retryAfterSeconds = 1, retryLimit = 3),
+                    HttpResponse(200, "{ in_app_messages: [] }"),
+                )
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.listInAppMessages("appId", "subscriptionId", "1234", mockSessionDurationProvider)
+
+            // Then
+            response shouldNotBe null
+            response!!.count() shouldBe 0
 
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+            coVerify(exactly = 1) {
+                mockHttpClient.get(
+                    "apps/appId/subscriptions/subscriptionId/iams",
+                    match {
+                        it.rywToken == "1234" && it.retryCount == null && it.sessionDuration == mockSessionDurationProvider()
+                    },
+                )
+            }
+
+            // Verify that the get method retried twice with the RYW token
+            coVerify(exactly = 3) {
+                mockHttpClient.get(
+                    "apps/appId/subscriptions/subscriptionId/iams",
+                    match {
+                        it.rywToken == "1234" && it.sessionDuration == mockSessionDurationProvider() && it.retryCount != null
+                    },
+                )
+            }
 
-        // When
-        val exception =
-            shouldThrowUnit<BackendException> {
-                inAppBackendService.sendIAMImpression("appId", "subscriptionId", "variantId", "messageId")
+            // Verify that the get method was retried the final time without the RYW token
+            coVerify(exactly = 1) {
+                mockHttpClient.get(
+                    "apps/appId/subscriptions/subscriptionId/iams",
+                    match {
+                        it.rywToken == null && it.sessionDuration == mockSessionDurationProvider() && it.retryCount == null
+                    },
+                )
             }
+        }
+
+        test("getIAMData successfully hydrates successful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery {
+                mockHttpClient.get(any(), any())
+            } returns
+                HttpResponse(
+                    200,
+                    "{html: \"html1\", display_duration: 123, styles: {remove_height_margin: true, remove_width_margin: true}}",
+                )
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response shouldNotBe null
+            response.shouldRetry shouldBe false
+            response.content shouldNotBe null
+            response.content!!.contentHtml shouldStartWith "html1"
+            response.content!!.displayDuration shouldBe 123
+            response.content!!.useHeightMargin shouldBe false
+            response.content!!.useWidthMargin shouldBe false
+            response.content!!.isFullBleed shouldBe true
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
+
+        test("getIAMData successfully hydrates successful response with no content") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response shouldNotBe null
+            response.shouldRetry shouldBe false
+            response.content shouldBe null
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
+
+        test("getIAMData successfully hydrates successful response with no style") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(200, "{html: \"html1\", display_duration: 123 }")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response shouldNotBe null
+            response.shouldRetry shouldBe false
+            response.content shouldNotBe null
+            response.content!!.contentHtml shouldStartWith "html1"
+            response.content!!.displayDuration shouldBe 123
+            response.content!!.useHeightMargin shouldBe true
+            response.content!!.useWidthMargin shouldBe true
+            response.content!!.isFullBleed shouldBe false
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
 
-        // Then
-        exception.statusCode shouldBe 409
-        exception.response shouldBe "{}"
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/impression",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeBool("first_impression") shouldBe true
-                },
-            )
+        test("getIAMData indicates retry when retryable response provided") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(500, null)
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response shouldNotBe null
+            response.shouldRetry shouldBe true
+            response.content shouldBe null
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
+
+        test("getIAMData indicates no retry when non-retryable response provided") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response shouldNotBe null
+            response.shouldRetry shouldBe false
+            response.content shouldBe null
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
+
+        test("getIAMData indicates no retry when retryable response provided more than 3 times") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(500, null)
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response1 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+            val response2 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+            val response3 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+            val response4 = inAppBackendService.getIAMData("appId", "messageId", "variantId")
+
+            // Then
+            response1 shouldNotBe null
+            response1.shouldRetry shouldBe true
+            response1.content shouldBe null
+            response2 shouldNotBe null
+            response2.shouldRetry shouldBe true
+            response2.content shouldBe null
+            response3 shouldNotBe null
+            response3.shouldRetry shouldBe true
+            response3.content shouldBe null
+            response4 shouldNotBe null
+            response4.shouldRetry shouldBe false
+            response4.content shouldBe null
+
+            coVerify(exactly = 4) { mockHttpClient.get("in_app_messages/messageId/variants/variantId/html?app_id=appId", any()) }
+        }
+
+        test("getIAMPreviewData successfully hydrates successful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery {
+                mockHttpClient.get(any(), any())
+            } returns
+                HttpResponse(
+                    200,
+                    "{html: \"html1\", display_duration: 123, styles: {remove_height_margin: true, remove_width_margin: true}}",
+                )
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMPreviewData("appId", "previewUUID")
+
+            // Then
+            response shouldNotBe null
+            response!!.contentHtml shouldStartWith "html1"
+            response!!.displayDuration shouldBe 123
+            response!!.useHeightMargin shouldBe false
+            response!!.useWidthMargin shouldBe false
+            response!!.isFullBleed shouldBe true
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/device_preview?preview_id=previewUUID&app_id=appId", any()) }
+        }
+
+        test("getIAMPreviewData returns no data when response is unsuccessful") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.get(any(), any()) } returns HttpResponse(404, null)
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val response = inAppBackendService.getIAMPreviewData("appId", "previewUUID")
+
+            // Then
+            response shouldBe null
+
+            coVerify(exactly = 1) { mockHttpClient.get("in_app_messages/device_preview?preview_id=previewUUID&app_id=appId", any()) }
         }
-    }
-
-    test("sendIAMPageImpression is successful when there is a successful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
-
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
-
-        // When
-        inAppBackendService.sendIAMPageImpression("appId", "subscriptionId", "variantId", "messageId", "pageId")
-
-        // Then
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/pageImpression",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeString("page_id") shouldBe "pageId"
-                },
-            )
+
+        test("sendIAMClick is successful when there is a successful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            inAppBackendService.sendIAMClick("appId", "subscriptionId", "variantId", "messageId", "clickId", isFirstClick = true)
+
+            // Then
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/click",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("click_id") shouldBe "clickId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeBool("first_click") shouldBe true
+                    },
+                )
+            }
         }
-    }
 
-    test("sendIAMPageImpression throws exception when there is an unsuccessful response") {
-        // Given
-        val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
-        val mockHttpClient = mockk<IHttpClient>()
-        coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
+        test("sendIAMClick throws exception when there is an unsuccessful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val exception =
+                shouldThrowUnit<BackendException> {
+                    inAppBackendService.sendIAMClick(
+                        "appId",
+                        "subscriptionId",
+                        "variantId",
+                        "messageId",
+                        "clickId",
+                        isFirstClick = true,
+                    )
+                }
+
+            // Then
+            exception.statusCode shouldBe 409
+            exception.response shouldBe "{}"
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/click",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("click_id") shouldBe "clickId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeBool("first_click") shouldBe true
+                    },
+                )
+            }
+        }
 
-        val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+        test("sendIAMImpression is successful when there is a successful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            inAppBackendService.sendIAMImpression("appId", "subscriptionId", "variantId", "messageId")
+
+            // Then
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/impression",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeBool("first_impression") shouldBe true
+                    },
+                )
+            }
+        }
+
+        test("sendIAMImpression throws exception when there is an unsuccessful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val exception =
+                shouldThrowUnit<BackendException> {
+                    inAppBackendService.sendIAMImpression("appId", "subscriptionId", "variantId", "messageId")
+                }
+
+            // Then
+            exception.statusCode shouldBe 409
+            exception.response shouldBe "{}"
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/impression",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeBool("first_impression") shouldBe true
+                    },
+                )
+            }
+        }
 
-        // When
-        val exception =
-            shouldThrowUnit<BackendException> {
-                inAppBackendService.sendIAMPageImpression("appId", "subscriptionId", "variantId", "messageId", "pageId")
+        test("sendIAMPageImpression is successful when there is a successful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(200, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            inAppBackendService.sendIAMPageImpression("appId", "subscriptionId", "variantId", "messageId", "pageId")
+
+            // Then
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/pageImpression",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeString("page_id") shouldBe "pageId"
+                    },
+                )
             }
+        }
 
-        // Then
-        exception.statusCode shouldBe 409
-        exception.response shouldBe "{}"
-        coVerify(exactly = 1) {
-            mockHttpClient.post(
-                "in_app_messages/messageId/pageImpression",
-                withArg {
-                    it.safeString("app_id") shouldBe "appId"
-                    it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
-                    it.safeString("player_id") shouldBe "subscriptionId"
-                    it.safeString("variant_id") shouldBe "variantId"
-                    it.safeString("page_id") shouldBe "pageId"
-                },
-            )
+        test("sendIAMPageImpression throws exception when there is an unsuccessful response") {
+            // Given
+            val mockHydrator = InAppHydrator(MockHelper.time(1000), MockHelper.propertiesModelStore())
+            val mockHttpClient = mockk<IHttpClient>()
+            coEvery { mockHttpClient.post(any(), any()) } returns HttpResponse(409, "{}")
+
+            val inAppBackendService = InAppBackendService(mockHttpClient, MockHelper.deviceService(), mockHydrator)
+
+            // When
+            val exception =
+                shouldThrowUnit<BackendException> {
+                    inAppBackendService.sendIAMPageImpression("appId", "subscriptionId", "variantId", "messageId", "pageId")
+                }
+
+            // Then
+            exception.statusCode shouldBe 409
+            exception.response shouldBe "{}"
+            coVerify(exactly = 1) {
+                mockHttpClient.post(
+                    "in_app_messages/messageId/pageImpression",
+                    withArg {
+                        it.safeString("app_id") shouldBe "appId"
+                        it.safeInt("device_type") shouldBe IDeviceService.DeviceType.Android.value
+                        it.safeString("player_id") shouldBe "subscriptionId"
+                        it.safeString("variant_id") shouldBe "variantId"
+                        it.safeString("page_id") shouldBe "pageId"
+                    },
+                )
+            }
         }
-    }
-})
+    })