diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
index 76707e352ad472aed566118f10963e6bb8600177..41e3cb1625df0053aefc702df9f176416f35645b 100644
--- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
+++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
@@ -18,6 +18,7 @@ public struct MessengerEnvironment {
   public var getFileTransferParams: GetFileTransferParams
   public var getNotificationsReport: GetNotificationsReport
   public var getSingleUseParams: GetSingleUseParams
+  public var groupRequests: GroupRequestCallbacksRegistry
   public var initFileTransfer: InitFileTransfer
   public var initializeBackup: InitializeBackup
   public var isListeningForMessages: Stored<Bool>
@@ -71,6 +72,7 @@ extension MessengerEnvironment {
       getFileTransferParams: .liveDefault,
       getNotificationsReport: .live,
       getSingleUseParams: .liveDefault,
+      groupRequests: .live(),
       initFileTransfer: .live,
       initializeBackup: .live,
       isListeningForMessages: .inMemory(false),
@@ -119,6 +121,7 @@ extension MessengerEnvironment {
     getFileTransferParams: .unimplemented,
     getNotificationsReport: .unimplemented,
     getSingleUseParams: .unimplemented,
+    groupRequests: .unimplemented,
     initFileTransfer: .unimplemented,
     initializeBackup: .unimplemented,
     isListeningForMessages: .unimplemented(placeholder: false),
diff --git a/Sources/XXMessengerClient/Utils/GroupRequestCallbacksRegistry.swift b/Sources/XXMessengerClient/Utils/GroupRequestCallbacksRegistry.swift
new file mode 100644
index 0000000000000000000000000000000000000000..06aff544a89ca68d345fcead12e43662d692e8a1
--- /dev/null
+++ b/Sources/XXMessengerClient/Utils/GroupRequestCallbacksRegistry.swift
@@ -0,0 +1,42 @@
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+
+public struct GroupRequestCallbacksRegistry {
+  public var register: (GroupRequest) -> Cancellable
+  public var registered: () -> GroupRequest
+}
+
+extension GroupRequestCallbacksRegistry {
+  public static func live() -> GroupRequestCallbacksRegistry {
+    class Registry {
+      var items: [UUID: GroupRequest] = [:]
+    }
+    let registry = Registry()
+    return GroupRequestCallbacksRegistry(
+      register: { groupRequest in
+        let id = UUID()
+        registry.items[id] = groupRequest
+        return Cancellable { registry.items[id] = nil }
+      },
+      registered: {
+        GroupRequest { group in
+          registry.items.values.forEach { $0.handle(group) }
+        }
+      }
+    )
+  }
+}
+
+extension GroupRequestCallbacksRegistry {
+  public static let unimplemented = GroupRequestCallbacksRegistry(
+    register: XCTestDynamicOverlay.unimplemented(
+      "\(Self.self).register",
+      placeholder: Cancellable {}
+    ),
+    registered: XCTestDynamicOverlay.unimplemented(
+      "\(Self.self).registered",
+      placeholder: .unimplemented
+    )
+  )
+}
diff --git a/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
index d36ad479f5f865ad5ff2e06656938740054b5676..bf4bdf72154944bb36b52e7bda74ae76ecce7757 100644
--- a/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
+++ b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
@@ -55,3 +55,11 @@ extension MessageService {
     )
   }
 }
+
+extension Group {
+  static func stub(_ id: Int) -> Group {
+    var group = Group.unimplemented
+    group.getId.run = { "group-\(id)".data(using: .utf8)! }
+    return group
+  }
+}
diff --git a/Tests/XXMessengerClientTests/Utils/GroupRequestCallbacksRegistryTests.swift b/Tests/XXMessengerClientTests/Utils/GroupRequestCallbacksRegistryTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..386f9d46ad5f95d276d8f812452b4a95f3a772ae
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Utils/GroupRequestCallbacksRegistryTests.swift
@@ -0,0 +1,46 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class GroupRequestCallbacksRegistryTests: XCTestCase {
+  func testRegistry() {
+    var firstCallbackDidHandle: [Group] = []
+    var secondCallbackDidHandle: [Group] = []
+
+    let firstCallback = GroupRequest { group in
+      firstCallbackDidHandle.append(group)
+    }
+    let secondCallback = GroupRequest { group in
+      secondCallbackDidHandle.append(group)
+    }
+    let registry: GroupRequestCallbacksRegistry = .live()
+    let registeredCallbacks = registry.registered()
+    let firstCallbackCancellable = registry.register(firstCallback)
+    let secondCallbackCancellable = registry.register(secondCallback)
+
+    let firstGroup = Group.stub(1)
+    registeredCallbacks.handle(firstGroup)
+
+    XCTAssertNoDifference(firstCallbackDidHandle.map { $0.getId() }, [firstGroup.getId()])
+    XCTAssertNoDifference(secondCallbackDidHandle.map { $0.getId() }, [firstGroup.getId()])
+
+    firstCallbackDidHandle = []
+    secondCallbackDidHandle = []
+    firstCallbackCancellable.cancel()
+    let secondGroup = Group.stub(2)
+    registeredCallbacks.handle(secondGroup)
+
+    XCTAssertNoDifference(firstCallbackDidHandle.map { $0.getId() }, [])
+    XCTAssertNoDifference(secondCallbackDidHandle.map { $0.getId() }, [secondGroup.getId()])
+
+    firstCallbackDidHandle = []
+    secondCallbackDidHandle = []
+    secondCallbackCancellable.cancel()
+    let thirdGroup = Group.stub(3)
+    registeredCallbacks.handle(thirdGroup)
+
+    XCTAssertNoDifference(firstCallbackDidHandle.map { $0.getId() }, [])
+    XCTAssertNoDifference(secondCallbackDidHandle.map { $0.getId() }, [])
+  }
+}