diff --git a/Sources/ElixxirDAppsSDK/Models/SingleUseCallbackReport.swift b/Sources/ElixxirDAppsSDK/Models/SingleUseCallbackReport.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a4acd5536503612e3260dc89e81088302e03d3da
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/Models/SingleUseCallbackReport.swift
@@ -0,0 +1,59 @@
+import Foundation
+
+public struct SingleUseCallbackReport: Equatable {
+  public init(
+    rounds: [Int],
+    payload: Data,
+    partner: Data,
+    ephId: EphId
+  ) {
+    self.rounds = rounds
+    self.payload = payload
+    self.partner = partner
+    self.ephId = ephId
+  }
+
+  public var rounds: [Int]
+  public var payload: Data
+  public var partner: Data
+  public var ephId: EphId
+}
+
+extension SingleUseCallbackReport {
+  public struct EphId: Equatable {
+    public init(
+      ephId: [Int],
+      source: Data
+    ) {
+      self.ephId = ephId
+      self.source = source
+    }
+
+    public var ephId: [Int]
+    public var source: Data
+  }
+}
+
+extension SingleUseCallbackReport: Codable {
+  enum CodingKeys: String, CodingKey {
+    case rounds = "Rounds"
+    case payload = "Payload"
+    case partner = "Partner"
+    case ephId = "EphID"
+  }
+
+  public static func decode(_ data: Data) throws -> Self {
+    try JSONDecoder().decode(Self.self, from: data)
+  }
+
+  public func encode() throws -> Data {
+    try JSONEncoder().encode(self)
+  }
+}
+
+extension SingleUseCallbackReport.EphId: Codable {
+  enum CodingKeys: String, CodingKey {
+    case ephId = "EphId"
+    case source = "Source"
+  }
+}
diff --git a/Tests/ElixxirDAppsSDKTests/Models/SingleUseCallbackReportTests.swift b/Tests/ElixxirDAppsSDKTests/Models/SingleUseCallbackReportTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..667644815969257ddcac1774222008312d5d73ec
--- /dev/null
+++ b/Tests/ElixxirDAppsSDKTests/Models/SingleUseCallbackReportTests.swift
@@ -0,0 +1,41 @@
+import CustomDump
+import XCTest
+@testable import ElixxirDAppsSDK
+
+final class SingleUseCallbackReportTests: XCTestCase {
+  func testCoding() throws {
+    let rounds: [Int] = [1, 5, 9]
+    let payloadB64 = "rSuPD35ELWwm5KTR9ViKIz/r1YGRgXIl5792SF8o8piZzN6sT4Liq4rUU/nfOPvQEjbfWNh/NYxdJ72VctDnWw=="
+    let partnerB64 = "emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
+    let ephId: [Int] = [0, 0, 0, 0, 0, 0, 3, 89]
+    let ephIdSourceB64 = "emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
+    let jsonString = """
+    {
+      "Rounds": [\(rounds.map { "\($0)" }.joined(separator: ", "))],
+      "Payload": "\(payloadB64)",
+      "Partner": "\(partnerB64)",
+      "EphID": {
+        "EphId": [\(ephId.map { "\($0)" }.joined(separator: ", "))],
+        "Source": "\(ephIdSourceB64)"
+      }
+    }
+    """
+    let jsonData = jsonString.data(using: .utf8)!
+    let report = try SingleUseCallbackReport.decode(jsonData)
+
+    XCTAssertNoDifference(report, SingleUseCallbackReport(
+      rounds: rounds,
+      payload: Data(base64Encoded: payloadB64)!,
+      partner: Data(base64Encoded: partnerB64)!,
+      ephId: .init(
+        ephId: ephId,
+        source: Data(base64Encoded: ephIdSourceB64)!
+      )
+    ))
+
+    let encodedReport = try report.encode()
+    let decodedReport = try SingleUseCallbackReport.decode(encodedReport)
+
+    XCTAssertNoDifference(decodedReport, report)
+  }
+}