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/Package.swift b/Examples/xx-messenger/Package.swift
index 2736c69c437e636e8eabcc3eefda2a96b9f238fc..716b1482e5d2f1081a20b6e748e3707b31229b26 100644
--- a/Examples/xx-messenger/Package.swift
+++ b/Examples/xx-messenger/Package.swift
@@ -21,6 +21,7 @@ let package = Package(
     .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"]),
@@ -115,6 +116,22 @@ let package = Package(
         .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: [
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 85d232415d82af42337d1fceea8eadb26369b000..0e5e54ad091bf310eb399f185b4c8680fed98e07 100644
--- a/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme
+++ b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme
@@ -59,6 +59,16 @@
                ReferencedContainer = "container:..">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "ConfirmRequestFeatureTests"
+               BuildableName = "ConfirmRequestFeatureTests"
+               BlueprintName = "ConfirmRequestFeatureTests"
+               ReferencedContainer = "container:..">
+            </BuildableReference>
+         </TestableReference>
          <TestableReference
             skipped = "NO">
             <BuildableReference
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
new file mode 100644
index 0000000000000000000000000000000000000000..b18f9da947115a2f6ebe503182430090cbf3445a
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
@@ -0,0 +1,94 @@
+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
+      do {
+        let e2e = try env.messenger.e2e.tryGet()
+        _ = try e2e.confirmReceivedRequest(partner: state.contact)
+        let contactId = try state.contact.getId()
+        try env.db().bulkUpdateContacts.callAsFunction(
+          .init(id: [contactId]),
+          .init(authStatus: .friend)
+        )
+        return .success(.didConfirm(.success))
+      } catch {
+        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/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2686d64531d147aae03a6b9b6fe888ef811153a0
--- /dev/null
+++ b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
@@ -0,0 +1,95 @@
+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])])
+    XCTAssertNoDifference(didBulkUpdateContactsWithAssignments, [.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()
+
+    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.send(.confirmTapped) {
+      $0.isConfirming = true
+      $0.result = nil
+    }
+
+    store.receive(.didConfirm(.failure(error.localizedDescription))) {
+      $0.isConfirming = false
+      $0.result = .failure(error.localizedDescription)
+    }
+  }
+}