diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift
index ae732094cc8cc4304781ad90406b1d151722567f..9d5e9a67a577cc535b3b350f4ff7c1e0008dbd5a 100644
--- a/Sources/ChatFeature/Controllers/GroupChatController.swift
+++ b/Sources/ChatFeature/Controllers/GroupChatController.swift
@@ -1,3 +1,4 @@
+import HUD
 import UIKit
 import Theme
 import Models
@@ -6,8 +7,10 @@ import Combine
 import XXModels
 import Voxophone
 import ChatLayout
+import Integration
 import DrawerFeature
 import DifferenceKit
+import ReportingFeature
 import ChatInputFeature
 import DependencyInjection
 
@@ -19,7 +22,11 @@ typealias OutgoingFailedGroupTextCell = CollectionCell<FlexibleSpace, StackMessa
 typealias OutgoingFailedGroupReplyCell = CollectionCell<FlexibleSpace, ReplyStackMessageView>
 
 public final class GroupChatController: UIViewController {
+    @Dependency private var hud: HUD
+    @Dependency private var session: SessionType
     @Dependency private var coordinator: ChatCoordinating
+    @Dependency private var makeReportDrawer: MakeReportDrawer
+    @Dependency private var makeAppScreenshot: MakeAppScreenshot
     @Dependency private var statusBarController: StatusBarStyleControlling
 
     private let members: MembersController
@@ -176,6 +183,17 @@ public final class GroupChatController: UIViewController {
                 }
             }.store(in: &cancellables)
 
+        viewModel.hudPublisher
+            .receive(on: DispatchQueue.main)
+            .sink { [hud] in hud.update(with: $0) }
+            .store(in: &cancellables)
+
+        viewModel.reportPopupPublisher
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] contact in
+                presentReportDrawer(contact)
+            }.store(in: &cancellables)
+
         viewModel.messages
             .receive(on: DispatchQueue.main)
             .sink { [unowned self] sections in
@@ -229,6 +247,19 @@ public final class GroupChatController: UIViewController {
         coordinator.toMembersList(members, from: self)
     }
 
+    private func presentReportDrawer(_ contact: Contact) {
+        var config = MakeReportDrawer.Config()
+        config.onReport = { [weak self] in
+            guard let self = self else { return }
+            let screenshot = try! self.makeAppScreenshot()
+            self.viewModel.report(contact: contact, screenshot: screenshot) {
+                self.collectionView.reloadData()
+            }
+        }
+        let drawer = makeReportDrawer(config)
+        coordinator.toDrawer(drawer, from: self)
+    }
+
     private func makeWaitingRoundDrawer() -> UIViewController {
         let text = DrawerText(
             font: Fonts.Mulish.semiBold.font(size: 14.0),
@@ -332,8 +363,7 @@ extension GroupChatController: UICollectionViewDataSource {
 
         var isSenderBanned = false
 
-        if let database = try? DependencyInjection.Container.shared.resolve() as Database,
-           let sender = try? database.fetchContacts(.init(id: [item.senderId])).first {
+        if let sender = try? session.dbManager.fetchContacts(.init(id: [item.senderId])).first {
             isSenderBanned = sender.isBanned
         }
 
@@ -568,6 +598,10 @@ extension GroupChatController: UICollectionViewDelegate {
                 self?.viewModel.didRequestDelete([item])
             }
 
+            let report = UIAction(title: Localized.Chat.BubbleMenu.report, state: .off) { [weak self] _ in
+                self?.viewModel.didRequestReport(item)
+            }
+
             let retry = UIAction(title: Localized.Chat.BubbleMenu.retry, state: .off) { [weak self] _ in
                 self?.viewModel.retry(item)
             }
@@ -579,7 +613,7 @@ extension GroupChatController: UICollectionViewDelegate {
             } else if item.status == .sending {
                 menu = UIMenu(title: "", children: [copy])
             } else {
-                menu = UIMenu(title: "", children: [copy, reply, delete])
+                menu = UIMenu(title: "", children: [copy, reply, delete, report])
             }
 
             return menu
diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
index ece776aea52bd2cb94e6ac8bcc6b2e7068b2bc3b..e9ca955393db78a4ccfc23bb52089032647bfcde 100644
--- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
+++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
@@ -1,10 +1,15 @@
+import HUD
 import UIKit
 import Models
+import Shared
 import Combine
 import XXModels
+import Defaults
 import Foundation
 import Integration
+import ToastFeature
 import DifferenceKit
+import ReportingFeature
 import DependencyInjection
 
 enum GroupChatNavigationRoutes: Equatable {
@@ -14,6 +19,18 @@ enum GroupChatNavigationRoutes: Equatable {
 
 final class GroupChatViewModel {
     @Dependency private var session: SessionType
+    @Dependency private var sendReport: SendReport
+    @Dependency private var toastController: ToastController
+
+    @KeyObject(.username, defaultValue: nil) var username: String?
+
+    var hudPublisher: AnyPublisher<HUDStatus, Never> {
+        hudSubject.eraseToAnyPublisher()
+    }
+
+    var reportPopupPublisher: AnyPublisher<Contact, Never> {
+        reportPopupSubject.eraseToAnyPublisher()
+    }
 
     var replyPublisher: AnyPublisher<(String, String), Never> {
         replySubject.eraseToAnyPublisher()
@@ -26,11 +43,13 @@ final class GroupChatViewModel {
     let info: GroupInfo
     private var stagedReply: Reply?
     private var cancellables = Set<AnyCancellable>()
+    private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
+    private let reportPopupSubject = PassthroughSubject<Contact, Never>()
     private let replySubject = PassthroughSubject<(String, String), Never>()
     private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>()
 
     var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> {
-        session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id), isSenderBanned: false))
+        session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id)))
             .assertNoFailure()
             .map { messages -> [ArraySection<ChatSection, Message>] in
                 let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in
@@ -63,6 +82,12 @@ final class GroupChatViewModel {
         _ = try? session.dbManager.deleteMessages(.init(id: Set(messages.map(\.id))))
     }
 
+    func didRequestReport(_ message: Message) {
+        if let contact = try? session.dbManager.fetchContacts(.init(id: [message.senderId])).first {
+            reportPopupSubject.send(contact)
+        }
+    }
+
     func send(_ text: String) {
         session.send(.init(
             text: text.trimmingCharacters(in: .whitespacesAndNewlines),
@@ -117,4 +142,57 @@ final class GroupChatViewModel {
         stagedReply = Reply(messageId: networkId, senderId: message.senderId)
         replySubject.send(getReplyContent(for: networkId))
     }
+
+    func report(contact: Contact, screenshot: UIImage, completion: @escaping () -> Void) {
+        let report = Report(
+            sender: .init(
+                userId: contact.id.base64EncodedString(),
+                username: contact.username!
+            ),
+            recipient: .init(
+                userId: session.myId.base64EncodedString(),
+                username: username!
+            ),
+            type: .group,
+            screenshot: screenshot.pngData()!,
+            partyName: info.group.name,
+            partyBlob: info.group.id.base64EncodedString(),
+            partyMembers: info.members.map { Report.ReportUser(
+                userId: $0.id.base64EncodedString(),
+                username: $0.username ?? "")
+            }
+        )
+
+        hudSubject.send(.on)
+        sendReport(report) { result in
+            switch result {
+            case .failure(let error):
+                DispatchQueue.main.async {
+                    self.hudSubject.send(.error(.init(with: error)))
+                }
+
+            case .success(_):
+                self.blockContact(contact)
+                DispatchQueue.main.async {
+                    self.hudSubject.send(.none)
+                    self.presentReportConfirmation(contact: contact)
+                    completion()
+                }
+            }
+        }
+    }
+
+    private func blockContact(_ contact: Contact) {
+        var contact = contact
+        contact.isBlocked = true
+        _ = try? session.dbManager.saveContact(contact)
+    }
+
+    private func presentReportConfirmation(contact: Contact) {
+        let name = (contact.nickname ?? contact.username) ?? "the contact"
+        toastController.enqueueToast(model: .init(
+            title: "Your report has been sent and \(name) is now blocked.",
+            leftImage: Asset.requestSentToaster.image
+        ))
+    }
 }
diff --git a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
index 0e9b4c5e5dac0bf0611e346f524071ef4f08f04d..d3918b04206d620af85be2266599369bf9ab85ea 100644
--- a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
+++ b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
@@ -139,15 +139,11 @@ final class ChatListViewModel {
                 ),
                 groupChatInfoQuery: GroupChatInfo.Query(
                     authStatus: [.participating],
-                    isLeaderBlocked: false,
-                    isLeaderBanned: false,
                     excludeBannedContactsMessages: true
                 ),
                 groupQuery: Group.Query(
                     withMessages: false,
-                    authStatus: [.participating],
-                    isLeaderBlocked: false,
-                    isLeaderBanned: false
+                    authStatus: [.participating]
                 )
             ))
             .assertNoFailure()
diff --git a/Sources/Integration/Session/Session+Group.swift b/Sources/Integration/Session/Session+Group.swift
index a3cf68964a307dceecb77893eb5f67fe15367bab..47652ee94cc4ec7d870005ff69d2d21ea3839ce3 100644
--- a/Sources/Integration/Session/Session+Group.swift
+++ b/Sources/Integration/Session/Session+Group.swift
@@ -53,7 +53,7 @@ extension Session {
                         recipientId: nil,
                         groupId: group.id,
                         date: group.createdAt,
-                        status: .received,
+                        status: .sent,
                         isUnread: false,
                         text: welcome,
                         replyMessageId: nil,
diff --git a/Sources/ReportingFeature/Report.swift b/Sources/ReportingFeature/Report.swift
index 61607f7b53e598b468c8f9ecbe23aab10cfb5b02..c2032b6a6b87d096f8f6883085d4c6e887630600 100644
--- a/Sources/ReportingFeature/Report.swift
+++ b/Sources/ReportingFeature/Report.swift
@@ -5,18 +5,27 @@ public struct Report: Encodable {
         sender: ReportUser,
         recipient: ReportUser,
         type: ReportType,
-        screenshot: Data
+        screenshot: Data,
+        partyName: String? = nil,
+        partyBlob: String? = nil,
+        partyMembers: [ReportUser]? = nil
     ) {
         self.sender = sender
         self.recipient = recipient
         self.type = type
         self.screenshot = screenshot
+        self.partyName = partyName
+        self.partyBlob = partyBlob
+        self.partyMembers = partyMembers
     }
 
     public var sender: ReportUser
     public var recipient: ReportUser
     public var type: ReportType
     public var screenshot: Data
+    public var partyName: String?
+    public var partyBlob: String?
+    public var partyMembers: [ReportUser]?
 }
 
 extension Report {
diff --git a/Sources/ReportingFeature/Resources/report_cert.der b/Sources/ReportingFeature/Resources/report_cert.der
index 978f65098ea8f361f1f369194e24d68d258f4dc2..b040579312ef63ed54d63419ad2955e6160b9666 100644
Binary files a/Sources/ReportingFeature/Resources/report_cert.der and b/Sources/ReportingFeature/Resources/report_cert.der differ