diff --git a/Sources/XXClient/Models/GroupChatMessage.swift b/Sources/XXClient/Models/GroupChatMessage.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8edeae042c5bd4a08612c6ce6310de54660d8dc1
--- /dev/null
+++ b/Sources/XXClient/Models/GroupChatMessage.swift
@@ -0,0 +1,41 @@
+import Foundation
+
+public struct GroupChatMessage: Equatable {
+  public init(
+    groupId: Data,
+    senderId: Data,
+    messageId: Data,
+    payload: Data,
+    timestamp: Int64
+  ) {
+    self.groupId = groupId
+    self.senderId = senderId
+    self.messageId = messageId
+    self.payload = payload
+    self.timestamp = timestamp
+  }
+
+  public var groupId: Data
+  public var senderId: Data
+  public var messageId: Data
+  public var payload: Data
+  public var timestamp: Int64
+}
+
+extension GroupChatMessage: Codable {
+  enum CodingKeys: String, CodingKey {
+    case groupId = "GroupId"
+    case senderId = "SenderId"
+    case messageId = "MessageId"
+    case payload = "Payload"
+    case timestamp = "Timestamp"
+  }
+
+  public static func decode(_ data: Data) throws -> Self {
+    try JSONDecoder().decode(Self.self, from: data)
+  }
+
+  public func encode() throws -> Data {
+    try JSONEncoder().encode(self)
+  }
+}
diff --git a/Tests/XXClientTests/Models/GroupChatMessageTests.swift b/Tests/XXClientTests/Models/GroupChatMessageTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..dff05388f50dc7ed23aaa4c674af957ed3e41484
--- /dev/null
+++ b/Tests/XXClientTests/Models/GroupChatMessageTests.swift
@@ -0,0 +1,37 @@
+import CustomDump
+import XCTest
+@testable import XXClient
+
+final class GroupChatMessageTests: XCTestCase {
+  func testCoding() throws {
+    let groupIdB64 = "AAAAAAAJlasAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE"
+    let senderIdB64 = "AAAAAAAAB8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
+    let messageIdB64 = "Zm9ydHkgZml2ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
+    let payloadB64 = "Zm9ydHkgZml2ZQ=="
+    let timestamp: Int64 = 1_663_009_269_474_079_000
+    let jsonString = """
+    {
+      "GroupId": "\(groupIdB64)",
+      "SenderId": "\(senderIdB64)",
+      "MessageId": "\(messageIdB64)",
+      "Payload": "\(payloadB64)",
+      "Timestamp": \(timestamp)
+    }
+    """
+    let jsonData = jsonString.data(using: .utf8)!
+    let model = try GroupChatMessage.decode(jsonData)
+
+    XCTAssertNoDifference(model, GroupChatMessage(
+      groupId: Data(base64Encoded: groupIdB64)!,
+      senderId: Data(base64Encoded: senderIdB64)!,
+      messageId: Data(base64Encoded: messageIdB64)!,
+      payload: Data(base64Encoded: payloadB64)!,
+      timestamp: timestamp
+    ))
+
+    let encodedModel = try model.encode()
+    let decodedModel = try GroupChatMessage.decode(encodedModel)
+
+    XCTAssertNoDifference(decodedModel, model)
+  }
+}