diff --git a/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/CheckContactAuthFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/CheckContactAuthFeature.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..17099adba157e5f3e5b943744dbe3cdf5b17706d
--- /dev/null
+++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/CheckContactAuthFeature.xcscheme
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1400"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CheckContactAuthFeature"
+               BuildableName = "CheckContactAuthFeature"
+               BlueprintName = "CheckContactAuthFeature"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CheckContactAuthFeatureTests"
+               BuildableName = "CheckContactAuthFeatureTests"
+               BlueprintName = "CheckContactAuthFeatureTests"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CheckContactAuthFeature"
+            BuildableName = "CheckContactAuthFeature"
+            BlueprintName = "CheckContactAuthFeature"
+            ReferencedContainer = "container:">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/ConfirmRequestFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/ConfirmRequestFeature.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..a3026c70355eab27e54fd62ebe8af6b508a45acd
--- /dev/null
+++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/ConfirmRequestFeature.xcscheme
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1400"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "ConfirmRequestFeature"
+               BuildableName = "ConfirmRequestFeature"
+               BlueprintName = "ConfirmRequestFeature"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "ConfirmRequestFeatureTests"
+               BuildableName = "ConfirmRequestFeatureTests"
+               BlueprintName = "ConfirmRequestFeatureTests"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "ConfirmRequestFeature"
+            BuildableName = "ConfirmRequestFeature"
+            BlueprintName = "ConfirmRequestFeature"
+            ReferencedContainer = "container:">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/VerifyContactFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/VerifyContactFeature.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..54cf617e7cd04c69782ad98a9873054c02982ec1
--- /dev/null
+++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/VerifyContactFeature.xcscheme
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1400"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "VerifyContactFeature"
+               BuildableName = "VerifyContactFeature"
+               BlueprintName = "VerifyContactFeature"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "VerifyContactFeatureTests"
+               BuildableName = "VerifyContactFeatureTests"
+               BlueprintName = "VerifyContactFeatureTests"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "VerifyContactFeature"
+            BuildableName = "VerifyContactFeature"
+            BlueprintName = "VerifyContactFeature"
+            ReferencedContainer = "container:">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift
index 57081f3ef7c5ae6abcf0cb130aa994d8a6460abc..5e9a48c35a590e8cb106ef2a0d8510f37779b977 100644
--- a/Examples/xx-messenger/Package.swift
+++ b/Examples/xx-messenger/Package.swift
@@ -20,6 +20,8 @@ let package = Package(
   products: [
     .library(name: "AppCore", targets: ["AppCore"]),
     .library(name: "AppFeature", targets: ["AppFeature"]),
+    .library(name: "CheckContactAuthFeature", targets: ["CheckContactAuthFeature"]),
+    .library(name: "ConfirmRequestFeature", targets: ["ConfirmRequestFeature"]),
     .library(name: "ContactFeature", targets: ["ContactFeature"]),
     .library(name: "ContactsFeature", targets: ["ContactsFeature"]),
     .library(name: "HomeFeature", targets: ["HomeFeature"]),
@@ -27,6 +29,7 @@ let package = Package(
     .library(name: "RestoreFeature", targets: ["RestoreFeature"]),
     .library(name: "SendRequestFeature", targets: ["SendRequestFeature"]),
     .library(name: "UserSearchFeature", targets: ["UserSearchFeature"]),
+    .library(name: "VerifyContactFeature", targets: ["VerifyContactFeature"]),
     .library(name: "WelcomeFeature", targets: ["WelcomeFeature"]),
   ],
   dependencies: [
@@ -73,6 +76,8 @@ let package = Package(
       name: "AppFeature",
       dependencies: [
         .target(name: "AppCore"),
+        .target(name: "CheckContactAuthFeature"),
+        .target(name: "ConfirmRequestFeature"),
         .target(name: "ContactFeature"),
         .target(name: "ContactsFeature"),
         .target(name: "HomeFeature"),
@@ -80,6 +85,7 @@ let package = Package(
         .target(name: "RestoreFeature"),
         .target(name: "SendRequestFeature"),
         .target(name: "UserSearchFeature"),
+        .target(name: "VerifyContactFeature"),
         .target(name: "WelcomeFeature"),
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
         .product(name: "ComposablePresentation", package: "swift-composable-presentation"),
@@ -95,11 +101,46 @@ let package = Package(
       ],
       swiftSettings: swiftSettings
     ),
+    .target(
+      name: "CheckContactAuthFeature",
+      dependencies: [
+        .target(name: "AppCore"),
+        .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
+        .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXModels", package: "client-ios-db"),
+      ]
+    ),
+    .testTarget(
+      name: "CheckContactAuthFeatureTests",
+      dependencies: [
+        .target(name: "CheckContactAuthFeature"),
+      ]
+    ),
+    .target(
+      name: "ConfirmRequestFeature",
+      dependencies: [
+        .target(name: "AppCore"),
+        .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
+        .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXModels", package: "client-ios-db"),
+      ]
+    ),
+    .testTarget(
+      name: "ConfirmRequestFeatureTests",
+      dependencies: [
+        .target(name: "ConfirmRequestFeature"),
+      ]
+    ),
     .target(
       name: "ContactFeature",
       dependencies: [
         .target(name: "AppCore"),
+        .target(name: "CheckContactAuthFeature"),
+        .target(name: "ConfirmRequestFeature"),
         .target(name: "SendRequestFeature"),
+        .target(name: "VerifyContactFeature"),
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
         .product(name: "ComposablePresentation", package: "swift-composable-presentation"),
         .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
@@ -223,6 +264,22 @@ let package = Package(
       ],
       swiftSettings: swiftSettings
     ),
+    .target(
+      name: "VerifyContactFeature",
+      dependencies: [
+        .target(name: "AppCore"),
+        .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
+        .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
+        .product(name: "XXModels", package: "client-ios-db"),
+      ]
+    ),
+    .testTarget(
+      name: "VerifyContactFeatureTests",
+      dependencies: [
+        .target(name: "VerifyContactFeature"),
+      ]
+    ),
     .target(
       name: "WelcomeFeature",
       dependencies: [
diff --git a/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme
index 9d31f6f70a3f7dd3e87b943466febea60557f6af..0e5e54ad091bf310eb399f185b4c8680fed98e07 100644
--- a/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme
+++ b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme
@@ -49,6 +49,26 @@
                ReferencedContainer = "container:..">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CheckContactAuthFeatureTests"
+               BuildableName = "CheckContactAuthFeatureTests"
+               BlueprintName = "CheckContactAuthFeatureTests"
+               ReferencedContainer = "container:..">
+            </BuildableReference>
+         </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "ConfirmRequestFeatureTests"
+               BuildableName = "ConfirmRequestFeatureTests"
+               BlueprintName = "ConfirmRequestFeatureTests"
+               ReferencedContainer = "container:..">
+            </BuildableReference>
+         </TestableReference>
          <TestableReference
             skipped = "NO">
             <BuildableReference
@@ -119,6 +139,16 @@
                ReferencedContainer = "container:..">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "VerifyContactFeatureTests"
+               BuildableName = "VerifyContactFeatureTests"
+               BlueprintName = "VerifyContactFeatureTests"
+               ReferencedContainer = "container:..">
+            </BuildableReference>
+         </TestableReference>
          <TestableReference
             skipped = "NO">
             <BuildableReference
diff --git a/Examples/xx-messenger/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift b/Examples/xx-messenger/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift
index d8ac3d18a496b64427b293fcc476c341d93353cb..6d1943e923732bc6dbdafe053deeb962191b7d13 100644
--- a/Examples/xx-messenger/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift
+++ b/Examples/xx-messenger/Sources/AppCore/AuthCallbackHandler/AuthCallbackHandlerRequest.swift
@@ -15,7 +15,6 @@ public struct AuthCallbackHandlerRequest {
 extension AuthCallbackHandlerRequest {
   public static func live(
     db: DBManagerGetDB,
-    messenger: Messenger,
     now: @escaping () -> Date
   ) -> AuthCallbackHandlerRequest {
     AuthCallbackHandlerRequest { xxContact in
@@ -28,21 +27,9 @@ extension AuthCallbackHandlerRequest {
       dbContact.username = try xxContact.getFact(.username)?.value
       dbContact.email = try xxContact.getFact(.email)?.value
       dbContact.phone = try xxContact.getFact(.phone)?.value
-      dbContact.authStatus = .verificationInProgress
+      dbContact.authStatus = .stranger
       dbContact.createdAt = now()
       dbContact = try db().saveContact(dbContact)
-
-      do {
-        try messenger.waitForNetwork()
-        try messenger.waitForNodes()
-        let verified = try messenger.verifyContact(xxContact)
-        dbContact.authStatus = verified ? .verified : .verificationFailed
-        dbContact = try db().saveContact(dbContact)
-      } catch {
-        dbContact.authStatus = .verificationFailed
-        dbContact = try db().saveContact(dbContact)
-        throw error
-      }
     }
   }
 }
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
index 5267fcc3f1a42445702386278b8fbeeb600785ae..7adbcae89b9a90d56d2297c29f63ba4be3700367 100644
--- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
@@ -1,4 +1,6 @@
 import AppCore
+import CheckContactAuthFeature
+import ConfirmRequestFeature
 import ContactFeature
 import ContactsFeature
 import Foundation
@@ -7,6 +9,7 @@ import RegisterFeature
 import RestoreFeature
 import SendRequestFeature
 import UserSearchFeature
+import VerifyContactFeature
 import WelcomeFeature
 import XXMessengerClient
 import XXModels
@@ -18,11 +21,7 @@ extension AppEnvironment {
     let messenger = Messenger.live(messengerEnv)
     let authHandler = AuthCallbackHandler.live(
       messenger: messenger,
-      handleRequest: .live(
-        db: dbManager.getDB,
-        messenger: messenger,
-        now: Date.init
-      ),
+      handleRequest: .live(db: dbManager.getDB, now: Date.init),
       handleConfirm: .live(db: dbManager.getDB),
       handleReset: .live(db: dbManager.getDB)
     )
@@ -41,6 +40,30 @@ extension AppEnvironment {
           mainQueue: mainQueue,
           bgQueue: bgQueue
         )
+      },
+      verifyContact: {
+        VerifyContactEnvironment(
+          messenger: messenger,
+          db: dbManager.getDB,
+          mainQueue: mainQueue,
+          bgQueue: bgQueue
+        )
+      },
+      confirmRequest: {
+        ConfirmRequestEnvironment(
+          messenger: messenger,
+          db: dbManager.getDB,
+          mainQueue: mainQueue,
+          bgQueue: bgQueue
+        )
+      },
+      checkAuth: {
+        CheckContactAuthEnvironment(
+          messenger: messenger,
+          db: dbManager.getDB,
+          mainQueue: mainQueue,
+          bgQueue: bgQueue
+        )
       }
     )
 
diff --git a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1f768be8d1cba4f20c1f2db65253788358a66023
--- /dev/null
+++ b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift
@@ -0,0 +1,94 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct CheckContactAuthState: Equatable {
+  public enum Result: Equatable {
+    case success(Bool)
+    case failure(String)
+  }
+
+  public init(
+    contact: XXClient.Contact,
+    isChecking: Bool = false,
+    result: Result? = nil
+  ) {
+    self.contact = contact
+    self.isChecking = isChecking
+    self.result = result
+  }
+
+  public var contact: XXClient.Contact
+  public var isChecking: Bool
+  public var result: Result?
+}
+
+public enum CheckContactAuthAction: Equatable {
+  case checkTapped
+  case didCheck(CheckContactAuthState.Result)
+}
+
+public struct CheckContactAuthEnvironment {
+  public init(
+    messenger: Messenger,
+    db: DBManagerGetDB,
+    mainQueue: AnySchedulerOf<DispatchQueue>,
+    bgQueue: AnySchedulerOf<DispatchQueue>
+  ) {
+    self.messenger = messenger
+    self.db = db
+    self.mainQueue = mainQueue
+    self.bgQueue = bgQueue
+  }
+
+  public var messenger: Messenger
+  public var db: DBManagerGetDB
+  public var mainQueue: AnySchedulerOf<DispatchQueue>
+  public var bgQueue: AnySchedulerOf<DispatchQueue>
+}
+
+#if DEBUG
+extension CheckContactAuthEnvironment {
+  public static let unimplemented = CheckContactAuthEnvironment(
+    messenger: .unimplemented,
+    db: .unimplemented,
+    mainQueue: .unimplemented,
+    bgQueue: .unimplemented
+  )
+}
+#endif
+
+public let checkContactAuthReducer = Reducer<CheckContactAuthState, CheckContactAuthAction, CheckContactAuthEnvironment>
+{ state, action, env in
+  switch action {
+  case .checkTapped:
+    state.isChecking = true
+    state.result = nil
+    return Effect.result { [state] in
+      do {
+        let e2e = try env.messenger.e2e.tryGet()
+        let contactId = try state.contact.getId()
+        let result = try e2e.hasAuthenticatedChannel(partnerId: contactId)
+        try env.db().bulkUpdateContacts.callAsFunction(
+          .init(id: [contactId]),
+          .init(authStatus: result ? .friend : .stranger)
+        )
+        return .success(.didCheck(.success(result)))
+      } catch {
+        return .success(.didCheck(.failure(error.localizedDescription)))
+      }
+    }
+    .subscribe(on: env.bgQueue)
+    .receive(on: env.mainQueue)
+    .eraseToEffect()
+
+  case .didCheck(let result):
+    state.isChecking = false
+    state.result = result
+    return .none
+  }
+}
diff --git a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..dd2e7894e256c67f4a1ddcf421877dcbfbac63cc
--- /dev/null
+++ b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift
@@ -0,0 +1,101 @@
+import ComposableArchitecture
+import SwiftUI
+import XXClient
+
+public struct CheckContactAuthView: View {
+  public init(store: Store<CheckContactAuthState, CheckContactAuthAction>) {
+    self.store = store
+  }
+
+  let store: Store<CheckContactAuthState, CheckContactAuthAction>
+
+  struct ViewState: Equatable {
+    var username: String?
+    var email: String?
+    var phone: String?
+    var isChecking: Bool
+    var result: CheckContactAuthState.Result?
+
+    init(state: CheckContactAuthState) {
+      username = try? state.contact.getFact(.username)?.value
+      email = try? state.contact.getFact(.email)?.value
+      phone = try? state.contact.getFact(.phone)?.value
+      isChecking = state.isChecking
+      result = state.result
+    }
+  }
+
+  public var body: some View {
+    WithViewStore(store.scope(state: ViewState.init)) { viewStore in
+      Form {
+        Section {
+          Label(viewStore.username ?? "", systemImage: "person")
+          Label(viewStore.email ?? "", systemImage: "envelope")
+          Label(viewStore.phone ?? "", systemImage: "phone")
+        } header: {
+          Text("Facts")
+        }
+
+        Section {
+          Button {
+            viewStore.send(.checkTapped)
+          } label: {
+            HStack {
+              Text("Check")
+              Spacer()
+              if viewStore.isChecking {
+                ProgressView()
+              } else {
+                Image(systemName: "play")
+              }
+            }
+          }
+          .disabled(viewStore.isChecking)
+        }
+
+        if let result = viewStore.result {
+          Section {
+            HStack {
+              switch result {
+              case .success(true):
+                Text("Authorized")
+                Spacer()
+                Image(systemName: "person.fill.checkmark")
+
+              case .success(false):
+                Text("Not authorized")
+                Spacer()
+                Image(systemName: "person.fill.xmark")
+
+              case .failure(_):
+                Text("Checking status failed")
+                Spacer()
+                Image(systemName: "xmark")
+              }
+            }
+            if case .failure(let failure) = result {
+              Text(failure)
+            }
+          } header: {
+            Text("Result")
+          }
+        }
+      }
+      .navigationTitle("Check connection")
+    }
+  }
+}
+
+#if DEBUG
+public struct CheckContactAuthView_Previews: PreviewProvider {
+  public static var previews: some View {
+    CheckContactAuthView(store: Store(
+      initialState: CheckContactAuthState(
+        contact: .unimplemented("contact-data".data(using: .utf8)!)
+      ),
+      reducer: .empty,
+      environment: ()
+    ))
+  }
+}
+#endif
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7cc40da0b7e268ce6ca78bb1b57b07e00784b148
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
@@ -0,0 +1,98 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct ConfirmRequestState: Equatable {
+  public enum Result: Equatable {
+    case success
+    case failure(String)
+  }
+
+  public init(
+    contact: XXClient.Contact,
+    isConfirming: Bool = false,
+    result: Result? = nil
+  ) {
+    self.contact = contact
+    self.isConfirming = isConfirming
+    self.result = result
+  }
+
+  public var contact: XXClient.Contact
+  public var isConfirming: Bool
+  public var result: Result?
+}
+
+public enum ConfirmRequestAction: Equatable {
+  case confirmTapped
+  case didConfirm(ConfirmRequestState.Result)
+}
+
+public struct ConfirmRequestEnvironment {
+  public init(
+    messenger: Messenger,
+    db: DBManagerGetDB,
+    mainQueue: AnySchedulerOf<DispatchQueue>,
+    bgQueue: AnySchedulerOf<DispatchQueue>
+  ) {
+    self.messenger = messenger
+    self.db = db
+    self.mainQueue = mainQueue
+    self.bgQueue = bgQueue
+  }
+
+  public var messenger: Messenger
+  public var db: DBManagerGetDB
+  public var mainQueue: AnySchedulerOf<DispatchQueue>
+  public var bgQueue: AnySchedulerOf<DispatchQueue>
+}
+
+#if DEBUG
+extension ConfirmRequestEnvironment {
+  public static let unimplemented = ConfirmRequestEnvironment(
+    messenger: .unimplemented,
+    db: .unimplemented,
+    mainQueue: .unimplemented,
+    bgQueue: .unimplemented
+  )
+}
+#endif
+
+public let confirmRequestReducer = Reducer<ConfirmRequestState, ConfirmRequestAction, ConfirmRequestEnvironment>
+{ state, action, env in
+  switch action {
+  case .confirmTapped:
+    state.isConfirming = true
+    state.result = nil
+    return Effect.result { [state] in
+      func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
+        try env.db().bulkUpdateContacts.callAsFunction(
+          .init(id: [try state.contact.getId()]),
+          .init(authStatus: status)
+        )
+      }
+      do {
+        try updateStatus(.confirming)
+        let e2e = try env.messenger.e2e.tryGet()
+        _ = try e2e.confirmReceivedRequest(partner: state.contact)
+        try updateStatus(.friend)
+        return .success(.didConfirm(.success))
+      } catch {
+        try? updateStatus(.confirmationFailed)
+        return .success(.didConfirm(.failure(error.localizedDescription)))
+      }
+    }
+    .subscribe(on: env.bgQueue)
+    .receive(on: env.mainQueue)
+    .eraseToEffect()
+
+  case .didConfirm(let result):
+    state.isConfirming = false
+    state.result = result
+    return .none
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..90ebc70e4490d51792db636a47d2e02b318f5820
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift
@@ -0,0 +1,95 @@
+import ComposableArchitecture
+import SwiftUI
+
+public struct ConfirmRequestView: View {
+  public init(store: Store<ConfirmRequestState, ConfirmRequestAction>) {
+    self.store = store
+  }
+
+  let store: Store<ConfirmRequestState, ConfirmRequestAction>
+
+  struct ViewState: Equatable {
+    var username: String?
+    var email: String?
+    var phone: String?
+    var isConfirming: Bool
+    var result: ConfirmRequestState.Result?
+
+    init(state: ConfirmRequestState) {
+      username = try? state.contact.getFact(.username)?.value
+      email = try? state.contact.getFact(.email)?.value
+      phone = try? state.contact.getFact(.phone)?.value
+      isConfirming = state.isConfirming
+      result = state.result
+    }
+  }
+
+  public var body: some View {
+    WithViewStore(store, observe: ViewState.init) { viewStore in
+      Form {
+        Section {
+          Label(viewStore.username ?? "", systemImage: "person")
+          Label(viewStore.email ?? "", systemImage: "envelope")
+          Label(viewStore.phone ?? "", systemImage: "phone")
+        } header: {
+          Text("Facts")
+        }
+
+        Section {
+          Button {
+            viewStore.send(.confirmTapped)
+          } label: {
+            HStack {
+              Text("Confirm")
+              Spacer()
+              if viewStore.isConfirming {
+                ProgressView()
+              } else {
+                Image(systemName: "checkmark")
+              }
+            }
+          }
+          .disabled(viewStore.isConfirming)
+        }
+
+        if let result = viewStore.result {
+          Section {
+            HStack {
+              switch result {
+              case .success:
+                Text("Request confirmed")
+                Spacer()
+                Image(systemName: "person.fill.checkmark")
+
+              case .failure(_):
+                Text("Confirming request failed")
+                Spacer()
+                Image(systemName: "xmark")
+              }
+            }
+            if case .failure(let failure) = result {
+              Text(failure)
+            }
+          } header: {
+            Text("Result")
+          }
+        }
+      }
+      .navigationTitle("Confirm request")
+    }
+  }
+}
+
+#if DEBUG
+public struct ConfirmRequestView_Previews: PreviewProvider {
+  public static var previews: some View {
+    ConfirmRequestView(store: Store(
+      initialState: ConfirmRequestState(
+        contact: .unimplemented("contact-data".data(using: .utf8)!)
+      ),
+      reducer: .empty,
+      environment: ()
+    ))
+  }
+}
+#endif
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
index 9f6ef294f5a8b35bdc7e7d0300ce05fd84ae1823..545cb8fb520e9f1a99e691855c78dd1b166e98a0 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
@@ -1,8 +1,11 @@
 import AppCore
+import CheckContactAuthFeature
 import ComposableArchitecture
 import ComposablePresentation
+import ConfirmRequestFeature
 import Foundation
 import SendRequestFeature
+import VerifyContactFeature
 import XCTestDynamicOverlay
 import XXClient
 import XXMessengerClient
@@ -16,7 +19,10 @@ public struct ContactState: Equatable {
     importUsername: Bool = true,
     importEmail: Bool = true,
     importPhone: Bool = true,
-    sendRequest: SendRequestState? = nil
+    sendRequest: SendRequestState? = nil,
+    verifyContact: VerifyContactState? = nil,
+    confirmRequest: ConfirmRequestState? = nil,
+    checkAuth: CheckContactAuthState? = nil
   ) {
     self.id = id
     self.dbContact = dbContact
@@ -25,6 +31,9 @@ public struct ContactState: Equatable {
     self.importEmail = importEmail
     self.importPhone = importPhone
     self.sendRequest = sendRequest
+    self.verifyContact = verifyContact
+    self.confirmRequest = confirmRequest
+    self.checkAuth = checkAuth
   }
 
   public var id: Data
@@ -34,6 +43,9 @@ public struct ContactState: Equatable {
   @BindableState public var importEmail: Bool
   @BindableState public var importPhone: Bool
   public var sendRequest: SendRequestState?
+  public var verifyContact: VerifyContactState?
+  public var confirmRequest: ConfirmRequestState?
+  public var checkAuth: CheckContactAuthState?
 }
 
 public enum ContactAction: Equatable, BindableAction {
@@ -43,6 +55,15 @@ public enum ContactAction: Equatable, BindableAction {
   case sendRequestTapped
   case sendRequestDismissed
   case sendRequest(SendRequestAction)
+  case verifyContactTapped
+  case verifyContactDismissed
+  case verifyContact(VerifyContactAction)
+  case checkAuthTapped
+  case checkAuthDismissed
+  case checkAuth(CheckContactAuthAction)
+  case confirmRequestTapped
+  case confirmRequestDismissed
+  case confirmRequest(ConfirmRequestAction)
   case binding(BindingAction<ContactState>)
 }
 
@@ -52,13 +73,19 @@ public struct ContactEnvironment {
     db: DBManagerGetDB,
     mainQueue: AnySchedulerOf<DispatchQueue>,
     bgQueue: AnySchedulerOf<DispatchQueue>,
-    sendRequest: @escaping () -> SendRequestEnvironment
+    sendRequest: @escaping () -> SendRequestEnvironment,
+    verifyContact: @escaping () -> VerifyContactEnvironment,
+    confirmRequest: @escaping () -> ConfirmRequestEnvironment,
+    checkAuth: @escaping () -> CheckContactAuthEnvironment
   ) {
     self.messenger = messenger
     self.db = db
     self.mainQueue = mainQueue
     self.bgQueue = bgQueue
     self.sendRequest = sendRequest
+    self.verifyContact = verifyContact
+    self.confirmRequest = confirmRequest
+    self.checkAuth = checkAuth
   }
 
   public var messenger: Messenger
@@ -66,6 +93,9 @@ public struct ContactEnvironment {
   public var mainQueue: AnySchedulerOf<DispatchQueue>
   public var bgQueue: AnySchedulerOf<DispatchQueue>
   public var sendRequest: () -> SendRequestEnvironment
+  public var verifyContact: () -> VerifyContactEnvironment
+  public var confirmRequest: () -> ConfirmRequestEnvironment
+  public var checkAuth: () -> CheckContactAuthEnvironment
 }
 
 #if DEBUG
@@ -75,7 +105,10 @@ extension ContactEnvironment {
     db: .unimplemented,
     mainQueue: .unimplemented,
     bgQueue: .unimplemented,
-    sendRequest: { .unimplemented }
+    sendRequest: { .unimplemented },
+    verifyContact: { .unimplemented },
+    confirmRequest: { .unimplemented },
+    checkAuth: { .unimplemented }
   )
 }
 #endif
@@ -135,10 +168,43 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
     state.sendRequest = nil
     return .none
 
-  case .sendRequest(_):
+  case .verifyContactTapped:
+    if let marshaled = state.dbContact?.marshaled {
+      state.verifyContact = VerifyContactState(
+        contact: .live(marshaled)
+      )
+    }
+    return .none
+
+  case .verifyContactDismissed:
+    state.verifyContact = nil
+    return .none
+
+  case .checkAuthTapped:
+    if let marshaled = state.dbContact?.marshaled {
+      state.checkAuth = CheckContactAuthState(
+        contact: .live(marshaled)
+      )
+    }
+    return .none
+
+  case .checkAuthDismissed:
+    state.checkAuth = nil
+    return .none
+
+  case .confirmRequestTapped:
+    if let marshaled = state.dbContact?.marshaled {
+      state.confirmRequest = ConfirmRequestState(
+        contact: .live(marshaled)
+      )
+    }
     return .none
 
-  case .binding(_):
+  case .confirmRequestDismissed:
+    state.confirmRequest = nil
+    return .none
+
+  case .binding(_), .sendRequest(_), .verifyContact(_), .confirmRequest(_), .checkAuth(_):
     return .none
   }
 }
@@ -150,3 +216,24 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
   action: /ContactAction.sendRequest,
   environment: { $0.sendRequest() }
 )
+.presenting(
+  verifyContactReducer,
+  state: .keyPath(\.verifyContact),
+  id: .notNil(),
+  action: /ContactAction.verifyContact,
+  environment: { $0.verifyContact() }
+)
+.presenting(
+  confirmRequestReducer,
+  state: .keyPath(\.confirmRequest),
+  id: .notNil(),
+  action: /ContactAction.confirmRequest,
+  environment: { $0.confirmRequest() }
+)
+.presenting(
+  checkContactAuthReducer,
+  state: .keyPath(\.checkAuth),
+  id: .notNil(),
+  action: /ContactAction.checkAuth,
+  environment: { $0.checkAuth() }
+)
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 1cd8fa2eda4f9058cd8ed333f15e15f7588cd2dc..08ae7eb8863b95bc9e5b3d0ef8e7f529b2f8b39e 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -1,8 +1,11 @@
 import AppCore
+import CheckContactAuthFeature
 import ComposableArchitecture
 import ComposablePresentation
+import ConfirmRequestFeature
 import SendRequestFeature
 import SwiftUI
+import VerifyContactFeature
 import XXClient
 import XXModels
 
@@ -114,6 +117,33 @@ public struct ContactView: View {
                 Image(systemName: "chevron.forward")
               }
             }
+            Button {
+              viewStore.send(.verifyContactTapped)
+            } label: {
+              HStack {
+                Text("Verify contact")
+                Spacer()
+                Image(systemName: "chevron.forward")
+              }
+            }
+            Button {
+              viewStore.send(.confirmRequestTapped)
+            } label: {
+              HStack {
+                Text("Confirm request")
+                Spacer()
+                Image(systemName: "chevron.forward")
+              }
+            }
+            Button {
+              viewStore.send(.checkAuthTapped)
+            } label: {
+              HStack {
+                Text("Check authorization")
+                Spacer()
+                Image(systemName: "chevron.forward")
+              }
+            }
           } header: {
             Text("Auth")
           }
@@ -131,6 +161,30 @@ public struct ContactView: View {
         onDeactivate: { viewStore.send(.sendRequestDismissed) },
         destination: SendRequestView.init(store:)
       ))
+      .background(NavigationLinkWithStore(
+        store.scope(
+          state: \.verifyContact,
+          action: ContactAction.verifyContact
+        ),
+        onDeactivate: { viewStore.send(.verifyContactDismissed) },
+        destination: VerifyContactView.init(store:)
+      ))
+      .background(NavigationLinkWithStore(
+        store.scope(
+          state: \.confirmRequest,
+          action: ContactAction.confirmRequest
+        ),
+        onDeactivate: { viewStore.send(.confirmRequestDismissed) },
+        destination: ConfirmRequestView.init(store:)
+      ))
+      .background(NavigationLinkWithStore(
+        store.scope(
+          state: \.checkAuth,
+          action: ContactAction.checkAuth
+        ),
+        onDeactivate: { viewStore.send(.checkAuthDismissed) },
+        destination: CheckContactAuthView.init(store:)
+      ))
     }
   }
 }
diff --git a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1663d155f59e9c8d7eb4b542de4255021620be4c
--- /dev/null
+++ b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift
@@ -0,0 +1,97 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct VerifyContactState: Equatable {
+  public enum Result: Equatable {
+    case success(Bool)
+    case failure(String)
+  }
+
+  public init(
+    contact: XXClient.Contact,
+    isVerifying: Bool = false,
+    result: Result? = nil
+  ) {
+    self.contact = contact
+    self.isVerifying = isVerifying
+    self.result = result
+  }
+
+  public var contact: XXClient.Contact
+  public var isVerifying: Bool
+  public var result: Result?
+}
+
+public enum VerifyContactAction: Equatable {
+  case verifyTapped
+  case didVerify(VerifyContactState.Result)
+}
+
+public struct VerifyContactEnvironment {
+  public init(
+    messenger: Messenger,
+    db: DBManagerGetDB,
+    mainQueue: AnySchedulerOf<DispatchQueue>,
+    bgQueue: AnySchedulerOf<DispatchQueue>
+  ) {
+    self.messenger = messenger
+    self.db = db
+    self.mainQueue = mainQueue
+    self.bgQueue = bgQueue
+  }
+
+  public var messenger: Messenger
+  public var db: DBManagerGetDB
+  public var mainQueue: AnySchedulerOf<DispatchQueue>
+  public var bgQueue: AnySchedulerOf<DispatchQueue>
+}
+
+#if DEBUG
+extension VerifyContactEnvironment {
+  public static let unimplemented = VerifyContactEnvironment(
+    messenger: .unimplemented,
+    db: .unimplemented,
+    mainQueue: .unimplemented,
+    bgQueue: .unimplemented
+  )
+}
+#endif
+
+public let verifyContactReducer = Reducer<VerifyContactState, VerifyContactAction, VerifyContactEnvironment>
+{ state, action, env in
+  switch action {
+  case .verifyTapped:
+    state.isVerifying = true
+    state.result = nil
+    return Effect.result { [state] in
+      func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
+        try env.db().bulkUpdateContacts.callAsFunction(
+          .init(id: [try state.contact.getId()]),
+          .init(authStatus: status)
+        )
+      }
+      do {
+        try updateStatus(.verificationInProgress)
+        let result = try env.messenger.verifyContact(state.contact)
+        try updateStatus(result ? .verified : .verificationFailed)
+        return .success(.didVerify(.success(result)))
+      } catch {
+        try? updateStatus(.verificationFailed)
+        return .success(.didVerify(.failure(error.localizedDescription)))
+      }
+    }
+    .subscribe(on: env.bgQueue)
+    .receive(on: env.mainQueue)
+    .eraseToEffect()
+
+  case .didVerify(let result):
+    state.isVerifying = false
+    state.result = result
+    return .none
+  }
+}
diff --git a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..32b81ab789a5408cb46f34574cca6bf7e12144c0
--- /dev/null
+++ b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift
@@ -0,0 +1,100 @@
+import ComposableArchitecture
+import SwiftUI
+
+public struct VerifyContactView: View {
+  public init(store: Store<VerifyContactState, VerifyContactAction>) {
+    self.store = store
+  }
+
+  let store: Store<VerifyContactState, VerifyContactAction>
+
+  struct ViewState: Equatable {
+    var username: String?
+    var email: String?
+    var phone: String?
+    var isVerifying: Bool
+    var result: VerifyContactState.Result?
+
+    init(state: VerifyContactState) {
+      username = try? state.contact.getFact(.username)?.value
+      email = try? state.contact.getFact(.email)?.value
+      phone = try? state.contact.getFact(.phone)?.value
+      isVerifying = state.isVerifying
+      result = state.result
+    }
+  }
+
+  public var body: some View {
+    WithViewStore(store, observe: ViewState.init) { viewStore in
+      Form {
+        Section {
+          Label(viewStore.username ?? "", systemImage: "person")
+          Label(viewStore.email ?? "", systemImage: "envelope")
+          Label(viewStore.phone ?? "", systemImage: "phone")
+        } header: {
+          Text("Facts")
+        }
+
+        Section {
+          Button {
+            viewStore.send(.verifyTapped)
+          } label: {
+            HStack {
+              Text("Verify")
+              Spacer()
+              if viewStore.isVerifying {
+                ProgressView()
+              } else {
+                Image(systemName: "play")
+              }
+            }
+          }
+          .disabled(viewStore.isVerifying)
+        }
+
+        if let result = viewStore.result {
+          Section {
+            HStack {
+              switch result {
+              case .success(true):
+                Text("Contact verified")
+                Spacer()
+                Image(systemName: "person.fill.checkmark")
+
+              case .success(false):
+                Text("Contact not verified")
+                Spacer()
+                Image(systemName: "person.fill.xmark")
+
+              case .failure(_):
+                Text("Verification failed")
+                Spacer()
+                Image(systemName: "xmark")
+              }
+            }
+            if case .failure(let failure) = result {
+              Text(failure)
+            }
+          } header: {
+            Text("Result")
+          }
+        }
+      }
+      .navigationTitle("Verify Contact")
+    }
+  }
+}
+
+#if DEBUG
+public struct VerifyContactView_Previews: PreviewProvider {
+  public static var previews: some View {
+    VerifyContactView(store: Store(
+      initialState: VerifyContactState(
+        contact: .unimplemented("contact-data".data(using: .utf8)!)
+      ),
+      reducer: .empty,
+      environment: ()
+    ))
+  }
+}
+#endif
diff --git a/Examples/xx-messenger/Tests/AppCoreTests/AuthCallbackHandler/AuthCallbackHandlerRequestTests.swift b/Examples/xx-messenger/Tests/AppCoreTests/AuthCallbackHandler/AuthCallbackHandlerRequestTests.swift
index 65786884bfbe0821343509f20bd1f0942998768d..3a7cb6fbf2d475eadfdc10aa854938fad247167b 100644
--- a/Examples/xx-messenger/Tests/AppCoreTests/AuthCallbackHandler/AuthCallbackHandlerRequestTests.swift
+++ b/Examples/xx-messenger/Tests/AppCoreTests/AuthCallbackHandler/AuthCallbackHandlerRequestTests.swift
@@ -10,17 +10,8 @@ final class AuthCallbackHandlerRequestTests: XCTestCase {
   func testRequestFromNewContact() throws {
     let now = Date()
     var didFetchContacts: [XXModels.Contact.Query] = []
-    var didVerifyContact: [XXClient.Contact] = []
     var didSaveContact: [XXModels.Contact] = []
 
-    var messenger: Messenger = .unimplemented
-    messenger.waitForNetwork.run = { _ in }
-    messenger.waitForNodes.run = { _, _, _, _ in }
-    messenger.verifyContact.run = { contact in
-      didVerifyContact.append(contact)
-      return true
-    }
-
     let request = AuthCallbackHandlerRequest.live(
       db: .init {
         var db: Database = .failing
@@ -34,7 +25,6 @@ final class AuthCallbackHandlerRequestTests: XCTestCase {
         }
         return db
       },
-      messenger: messenger,
       now: { now }
     )
     var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
@@ -50,26 +40,15 @@ final class AuthCallbackHandlerRequestTests: XCTestCase {
     try request(xxContact)
 
     XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
-    XCTAssertNoDifference(didSaveContact, [
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        username: "username",
-        email: "email",
-        phone: "phone",
-        authStatus: .verificationInProgress,
-        createdAt: now
-      ),
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        username: "username",
-        email: "email",
-        phone: "phone",
-        authStatus: .verified,
-        createdAt: now
-      )
-    ])
+    XCTAssertNoDifference(didSaveContact, [.init(
+      id: "id".data(using: .utf8)!,
+      marshaled: "contact".data(using: .utf8)!,
+      username: "username",
+      email: "email",
+      phone: "phone",
+      authStatus: .stranger,
+      createdAt: now
+    )])
   }
 
   func testRequestWhenContactInDatabase() throws {
@@ -79,7 +58,6 @@ final class AuthCallbackHandlerRequestTests: XCTestCase {
         db.fetchContacts.run = { _ in [.init(id: "id".data(using: .utf8)!)] }
         return db
       },
-      messenger: .unimplemented,
       now: XCTUnimplemented("now", placeholder: Date())
     )
     var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
@@ -87,96 +65,4 @@ final class AuthCallbackHandlerRequestTests: XCTestCase {
 
     try request(contact)
   }
-
-  func testRequestFromNewContactVerificationFalse() throws {
-    let now = Date()
-    var didSaveContact: [XXModels.Contact] = []
-
-    var messenger: Messenger = .unimplemented
-    messenger.waitForNetwork.run = { _ in }
-    messenger.waitForNodes.run = { _, _, _, _ in }
-    messenger.verifyContact.run = { _ in false }
-
-    let request = AuthCallbackHandlerRequest.live(
-      db: .init {
-        var db: Database = .failing
-        db.fetchContacts.run = { query in return [] }
-        db.saveContact.run = { contact in
-          didSaveContact.append(contact)
-          return contact
-        }
-        return db
-      },
-      messenger: messenger,
-      now: { now }
-    )
-    var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
-    xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
-    xxContact.getFactsFromContact.run = { _ in [] }
-
-    try request(xxContact)
-
-    XCTAssertNoDifference(didSaveContact, [
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        authStatus: .verificationInProgress,
-        createdAt: now
-      ),
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        authStatus: .verificationFailed,
-        createdAt: now
-      )
-    ])
-  }
-
-  func testRequestFromNewContactVerificationFailure() throws {
-    struct Failure: Error, Equatable {}
-    let failure = Failure()
-    let now = Date()
-    var didSaveContact: [XXModels.Contact] = []
-
-    var messenger: Messenger = .unimplemented
-    messenger.waitForNetwork.run = { _ in }
-    messenger.waitForNodes.run = { _, _, _, _ in }
-    messenger.verifyContact.run = { _ in throw failure }
-
-    let request = AuthCallbackHandlerRequest.live(
-      db: .init {
-        var db: Database = .failing
-        db.fetchContacts.run = { query in return [] }
-        db.saveContact.run = { contact in
-          didSaveContact.append(contact)
-          return contact
-        }
-        return db
-      },
-      messenger: messenger,
-      now: { now }
-    )
-    var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
-    xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
-    xxContact.getFactsFromContact.run = { _ in [] }
-
-    XCTAssertThrowsError(try request(xxContact)) { error in
-      XCTAssertNoDifference(error as? Failure, failure)
-    }
-
-    XCTAssertNoDifference(didSaveContact, [
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        authStatus: .verificationInProgress,
-        createdAt: now
-      ),
-      .init(
-        id: "id".data(using: .utf8)!,
-        marshaled: "contact".data(using: .utf8)!,
-        authStatus: .verificationFailed,
-        createdAt: now
-      )
-    ])
-  }
 }
diff --git a/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift b/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..95f5a80773838e9b72d4f8c6e380e0f7ce8799fd
--- /dev/null
+++ b/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift
@@ -0,0 +1,147 @@
+import ComposableArchitecture
+import CustomDump
+import XCTest
+import XXClient
+import XXModels
+@testable import CheckContactAuthFeature
+
+final class CheckContactAuthFeatureTests: XCTestCase {
+  func testCheck() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: CheckContactAuthState(
+        contact: contact
+      ),
+      reducer: checkContactAuthReducer,
+      environment: .unimplemented
+    )
+
+    var didCheckPartnerId: [Data] = []
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.hasAuthenticatedChannel.run = { partnerId in
+        didCheckPartnerId.append(partnerId)
+        return true
+      }
+      return e2e
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.checkTapped) {
+      $0.isChecking = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didCheckPartnerId, [contactId])
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [.init(id: [contactId])])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [.init(authStatus: .friend)])
+
+    store.receive(.didCheck(.success(true))) {
+      $0.isChecking = false
+      $0.result = .success(true)
+    }
+  }
+
+  func testCheckNoConnection() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: CheckContactAuthState(
+        contact: contact
+      ),
+      reducer: checkContactAuthReducer,
+      environment: .unimplemented
+    )
+
+    var didCheckPartnerId: [Data] = []
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.hasAuthenticatedChannel.run = { partnerId in
+        didCheckPartnerId.append(partnerId)
+        return false
+      }
+      return e2e
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.checkTapped) {
+      $0.isChecking = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didCheckPartnerId, [contactId])
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [.init(id: [contactId])])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [.init(authStatus: .stranger)])
+
+    store.receive(.didCheck(.success(false))) {
+      $0.isChecking = false
+      $0.result = .success(false)
+    }
+  }
+
+  func testCheckFailure() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: CheckContactAuthState(
+        contact: contact
+      ),
+      reducer: checkContactAuthReducer,
+      environment: .unimplemented
+    )
+
+    struct Failure: Error {}
+    let error = Failure()
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.hasAuthenticatedChannel.run = { _ in throw error }
+      return e2e
+    }
+
+    store.send(.checkTapped) {
+      $0.isChecking = true
+      $0.result = nil
+    }
+
+    store.receive(.didCheck(.failure(error.localizedDescription))) {
+      $0.isChecking = false
+      $0.result = .failure(error.localizedDescription)
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8dea06e119b266534d4b5c6919023ce69789a762
--- /dev/null
+++ b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
@@ -0,0 +1,122 @@
+import ComposableArchitecture
+import CustomDump
+import XCTest
+import XXClient
+import XXModels
+@testable import ConfirmRequestFeature
+
+final class ConfirmRequestFeatureTests: XCTestCase {
+  func testConfirm() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: ConfirmRequestState(
+        contact: contact
+      ),
+      reducer: confirmRequestReducer,
+      environment: .unimplemented
+    )
+
+    var didConfirmRequestFromContact: [XXClient.Contact] = []
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.confirmReceivedRequest.run = { contact in
+        didConfirmRequestFromContact.append(contact)
+        return 0
+      }
+      return e2e
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.confirmTapped) {
+      $0.isConfirming = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didConfirmRequestFromContact, [contact])
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [
+      .init(id: [contactId]),
+      .init(id: [contactId]),
+    ])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [
+      .init(authStatus: .confirming),
+      .init(authStatus: .friend),
+    ])
+
+    store.receive(.didConfirm(.success)) {
+      $0.isConfirming = false
+      $0.result = .success
+    }
+  }
+
+  func testConfirmFailure() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: ConfirmRequestState(
+        contact: contact
+      ),
+      reducer: confirmRequestReducer,
+      environment: .unimplemented
+    )
+
+    struct Failure: Error {}
+    let error = Failure()
+
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.confirmReceivedRequest.run = { _ in throw error }
+      return e2e
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.confirmTapped) {
+      $0.isConfirming = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [
+      .init(id: [contactId]),
+      .init(id: [contactId]),
+    ])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [
+      .init(authStatus: .confirming),
+      .init(authStatus: .confirmationFailed),
+    ])
+
+    store.receive(.didConfirm(.failure(error.localizedDescription))) {
+      $0.isConfirming = false
+      $0.result = .failure(error.localizedDescription)
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
index 247abb49e226acd5ea720d8a7cd8265f6ca197ef..afc146b1981cf07e5c0105da1c6022f51c56aef4 100644
--- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
@@ -1,7 +1,10 @@
+import CheckContactAuthFeature
 import Combine
 import ComposableArchitecture
+import ConfirmRequestFeature
 import CustomDump
 import SendRequestFeature
+import VerifyContactFeature
 import XCTest
 import XXClient
 import XXModels
@@ -163,4 +166,118 @@ final class ContactFeatureTests: XCTestCase {
       $0.sendRequest = nil
     }
   }
+
+  func testVerifyContactTapped() {
+    let contactData = "contact-data".data(using: .utf8)!
+    let store = TestStore(
+      initialState: ContactState(
+        id: Data(),
+        dbContact: XXModels.Contact(
+          id: Data(),
+          marshaled: contactData
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.verifyContactTapped) {
+      $0.verifyContact = VerifyContactState(
+        contact: .unimplemented(contactData)
+      )
+    }
+  }
+
+  func testVerifyContactDismissed() {
+    let store = TestStore(
+      initialState: ContactState(
+        id: "contact-id".data(using: .utf8)!,
+        verifyContact: VerifyContactState(
+          contact: .unimplemented("contact-data".data(using: .utf8)!)
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.verifyContactDismissed) {
+      $0.verifyContact = nil
+    }
+  }
+
+  func testCheckAuthTapped() {
+    let contactData = "contact-data".data(using: .utf8)!
+    let store = TestStore(
+      initialState: ContactState(
+        id: Data(),
+        dbContact: XXModels.Contact(
+          id: Data(),
+          marshaled: contactData
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.checkAuthTapped) {
+      $0.checkAuth = CheckContactAuthState(
+        contact: .unimplemented(contactData)
+      )
+    }
+  }
+
+  func testCheckAuthDismissed() {
+    let store = TestStore(
+      initialState: ContactState(
+        id: "contact-id".data(using: .utf8)!,
+        checkAuth: CheckContactAuthState(
+          contact: .unimplemented("contact-data".data(using: .utf8)!)
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.checkAuthDismissed) {
+      $0.checkAuth = nil
+    }
+  }
+
+  func testConfirmRequestTapped() {
+    let contactData = "contact-data".data(using: .utf8)!
+    let store = TestStore(
+      initialState: ContactState(
+        id: Data(),
+        dbContact: XXModels.Contact(
+          id: Data(),
+          marshaled: contactData
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.confirmRequestTapped) {
+      $0.confirmRequest = ConfirmRequestState(
+        contact: .unimplemented(contactData)
+      )
+    }
+  }
+
+  func testConfirmRequestDismissed() {
+    let store = TestStore(
+      initialState: ContactState(
+        id: "contact-id".data(using: .utf8)!,
+        confirmRequest: ConfirmRequestState(
+          contact: .unimplemented("contact-data".data(using: .utf8)!)
+        )
+      ),
+      reducer: contactReducer,
+      environment: .unimplemented
+    )
+
+    store.send(.confirmRequestDismissed) {
+      $0.confirmRequest = nil
+    }
+  }
 }
diff --git a/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift b/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..97f8b428e53ffc63ae123bf9f8510594cc0f5daf
--- /dev/null
+++ b/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift
@@ -0,0 +1,168 @@
+import ComposableArchitecture
+import CustomDump
+import XCTest
+import XXClient
+import XXModels
+@testable import VerifyContactFeature
+
+final class VerifyContactFeatureTests: XCTestCase {
+  func testVerify() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: VerifyContactState(
+        contact: contact
+      ),
+      reducer: verifyContactReducer,
+      environment: .unimplemented
+    )
+
+    var didVerifyContact: [XXClient.Contact] = []
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.verifyContact.run = { contact in
+      didVerifyContact.append(contact)
+      return true
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.verifyTapped) {
+      $0.isVerifying = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didVerifyContact, [contact])
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [
+      .init(id: [contactId]),
+      .init(id: [contactId]),
+    ])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [
+      .init(authStatus: .verificationInProgress),
+      .init(authStatus: .verified)
+    ])
+
+    store.receive(.didVerify(.success(true))) {
+      $0.isVerifying = false
+      $0.result = .success(true)
+    }
+  }
+
+  func testVerifyNotVerified() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: VerifyContactState(
+        contact: contact
+      ),
+      reducer: verifyContactReducer,
+      environment: .unimplemented
+    )
+
+    var didVerifyContact: [XXClient.Contact] = []
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.verifyContact.run = { contact in
+      didVerifyContact.append(contact)
+      return false
+    }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.verifyTapped) {
+      $0.isVerifying = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didVerifyContact, [contact])
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [
+      .init(id: [contactId]),
+      .init(id: [contactId]),
+    ])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [
+      .init(authStatus: .verificationInProgress),
+      .init(authStatus: .verificationFailed),
+    ])
+
+    store.receive(.didVerify(.success(false))) {
+      $0.isVerifying = false
+      $0.result = .success(false)
+    }
+  }
+
+  func testVerifyFailure() {
+    var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
+    let contactId = "contact-id".data(using: .utf8)!
+    contact.getIdFromContact.run = { _ in contactId }
+
+    let store = TestStore(
+      initialState: VerifyContactState(
+        contact: contact
+      ),
+      reducer: verifyContactReducer,
+      environment: .unimplemented
+    )
+
+    struct Failure: Error {}
+    let error = Failure()
+
+    var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
+    var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
+
+    store.environment.mainQueue = .immediate
+    store.environment.bgQueue = .immediate
+    store.environment.messenger.verifyContact.run = { _ in throw error }
+    store.environment.db.run = {
+      var db: Database = .failing
+      db.bulkUpdateContacts.run = { query, assignments in
+        didBulkUpdateContactsWithQuery.append(query)
+        didBulkUpdateContactsWithAssignments.append(assignments)
+        return 0
+      }
+      return db
+    }
+
+    store.send(.verifyTapped) {
+      $0.isVerifying = true
+      $0.result = nil
+    }
+
+    XCTAssertNoDifference(didBulkUpdateContactsWithQuery, [
+      .init(id: [contactId]),
+      .init(id: [contactId]),
+    ])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [
+      .init(authStatus: .verificationInProgress),
+      .init(authStatus: .verificationFailed),
+    ])
+
+    store.receive(.didVerify(.failure(error.localizedDescription))) {
+      $0.isVerifying = false
+      $0.result = .failure(error.localizedDescription)
+    }
+  }
+}