diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift
index 91230c63f1aacc36064a366ecfca110e36f07b60..ef4290f829d7811c4e0c94c31afa812c095edfbc 100644
--- a/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift
@@ -3,24 +3,79 @@ import XCTestDynamicOverlay
 import XXClient
 
 public struct MessengerReceiveFile {
+  public struct Params: Equatable {
+    public init(
+      transferId: Data,
+      callbackIntervalMS: Int = 250
+    ) {
+      self.transferId = transferId
+      self.callbackIntervalMS = callbackIntervalMS
+    }
+
+    public var transferId: Data
+    public var callbackIntervalMS: Int
+  }
+
+  public enum CallbackInfo: Equatable {
+    public enum Failure: Equatable {
+      case callbackError(NSError)
+      case progressError(String)
+      case receiveError(NSError)
+    }
+
+    case progress(transmitted: Int, total: Int)
+    case finished(Data)
+    case failed(Failure)
+  }
+
+  public typealias Callback = (CallbackInfo) -> Void
+
   public enum Error: Swift.Error, Equatable {
-    case notConnected
+    case fileTransferNotStarted
   }
 
-  public var run: () throws -> Void
+  public var run: (Params, @escaping Callback) throws -> Void
 
-  public func callAsFunction() throws -> Void {
-    try run()
+  public func callAsFunction(
+    _ params: Params,
+    callback: @escaping Callback
+  ) throws -> Void {
+    try run(params, callback)
   }
 }
 
 extension MessengerReceiveFile {
   public static func live(_ env: MessengerEnvironment) -> MessengerReceiveFile {
-    MessengerReceiveFile {
-      guard let e2e = env.e2e() else {
-        throw Error.notConnected
+    MessengerReceiveFile { params, callback in
+      guard let fileTransfer = env.fileTransfer() else {
+        throw Error.fileTransferNotStarted
       }
-      // TODO: implement receiving file
+      try fileTransfer.registerReceivedProgressCallback(
+        transferId: params.transferId,
+        period: params.callbackIntervalMS,
+        callback: FileTransferProgressCallback { result in
+          switch result {
+          case .success(let info):
+            if let error = info.progress.error {
+              callback(.failed(.progressError(error)))
+            } else if info.progress.completed {
+              do {
+                callback(.finished(try fileTransfer.receive(transferId: params.transferId)))
+              } catch {
+                callback(.failed(.receiveError(error as NSError)))
+              }
+            } else {
+              callback(.progress(
+                transmitted: info.progress.transmitted,
+                total: info.progress.total
+              ))
+            }
+
+          case .failure(let error):
+            callback(.failed(.callbackError(error)))
+          }
+        }
+      )
     }
   }
 }
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift
index ef9b586421ebec8453ce7d2ec17e0f90a1ff6ef4..554b7f309114fbdaa2ea2679d290d09f238eaa52 100644
--- a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift
@@ -5,23 +5,219 @@ import XXClient
 
 final class MessengerReceiveFileTests: XCTestCase {
   func testReceiveFile() throws {
+    let params: MessengerReceiveFile.Params = .stub
+    let receivedData = "received".data(using: .utf8)!
+
+    var didRegisterReceivedProgressCallbackWithTransferId: [Data] = []
+    var didRegisterReceivedProgressCallbackWithPeriod: [Int] = []
+    var didRegisterReceivedProgressCallbackWithCallback: [FileTransferProgressCallback] = []
+    var didReceiveTransferId: [Data] = []
+    var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = []
+
     var env: MessengerEnvironment = .unimplemented
-    env.e2e.get = { .unimplemented }
+    env.fileTransfer.get = {
+      var fileTransfer: FileTransfer = .unimplemented
+      fileTransfer.registerReceivedProgressCallback.run = { transferId, period, callback in
+        didRegisterReceivedProgressCallbackWithTransferId.append(transferId)
+        didRegisterReceivedProgressCallbackWithPeriod.append(period)
+        didRegisterReceivedProgressCallbackWithCallback.append(callback)
+      }
+      fileTransfer.receive.run = { transferId in
+        didReceiveTransferId.append(transferId)
+        return receivedData
+      }
+      return fileTransfer
+    }
     let receiveFile: MessengerReceiveFile = .live(env)
 
-    try receiveFile()
+    try receiveFile(params) { info in
+      didReceiveCallback.append(info)
+    }
+
+    XCTAssertNoDifference(didRegisterReceivedProgressCallbackWithTransferId, [
+      params.transferId
+    ])
+    XCTAssertNoDifference(didRegisterReceivedProgressCallbackWithPeriod, [
+      params.callbackIntervalMS
+    ])
+    XCTAssertNoDifference(didReceiveCallback, [])
+
+    didReceiveCallback = []
+    didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success(
+      FileTransferProgressCallback.Callback(
+        progress: Progress(
+          completed: false,
+          transmitted: 1,
+          total: 3,
+          error: nil
+        ),
+        partTracker: .unimplemented
+      )
+    ))
+
+    XCTAssertNoDifference(didReceiveCallback, [
+      .progress(transmitted: 1, total: 3),
+    ])
+
+    didReceiveCallback = []
+    didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success(
+      FileTransferProgressCallback.Callback(
+        progress: Progress(
+          completed: false,
+          transmitted: 2,
+          total: 3,
+          error: nil
+        ),
+        partTracker: .unimplemented
+      )
+    ))
+
+    XCTAssertNoDifference(didReceiveCallback, [
+      .progress(transmitted: 2, total: 3),
+    ])
+
+    didReceiveCallback = []
+    didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success(
+      FileTransferProgressCallback.Callback(
+        progress: Progress(
+          completed: true,
+          transmitted: 3,
+          total: 3,
+          error: nil
+        ),
+        partTracker: .unimplemented
+      )
+    ))
+
+    XCTAssertNoDifference(didReceiveTransferId, [
+      params.transferId,
+    ])
+    XCTAssertNoDifference(didReceiveCallback, [
+      .finished(receivedData),
+    ])
   }
 
   func testReceiveFileWhenNotConnected() {
     var env: MessengerEnvironment = .unimplemented
-    env.e2e.get = { nil }
+    env.fileTransfer.get = { nil }
     let receiveFile: MessengerReceiveFile = .live(env)
 
-    XCTAssertThrowsError(try receiveFile()) { error in
+    XCTAssertThrowsError(try receiveFile(.stub) { _ in }) { error in
       XCTAssertNoDifference(
         error as? MessengerReceiveFile.Error,
-        MessengerReceiveFile.Error.notConnected
+        MessengerReceiveFile.Error.fileTransferNotStarted
       )
     }
   }
+
+  func testReceiveFileProgressError() throws {
+    let error = "Something went wrong..."
+
+    var receivedProgressCallback: FileTransferProgressCallback?
+    var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = []
+
+    var env: MessengerEnvironment = .unimplemented
+    env.fileTransfer.get = {
+      var fileTransfer: FileTransfer = .unimplemented
+      fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in
+        receivedProgressCallback = callback
+      }
+      return fileTransfer
+    }
+    let receiveFile: MessengerReceiveFile = .live(env)
+
+    try receiveFile(.stub) { info in
+      didReceiveCallback.append(info)
+    }
+
+    receivedProgressCallback?.handle(.success(
+      FileTransferProgressCallback.Callback(
+        progress: Progress(
+          completed: false,
+          transmitted: 1,
+          total: 3,
+          error: error
+        ),
+        partTracker: .unimplemented
+      )
+    ))
+
+    XCTAssertNoDifference(didReceiveCallback, [
+      .failed(.progressError(error))
+    ])
+  }
+
+  func testReceiveFileCallbackError() throws {
+    let error = NSError(domain: "test", code: 123)
+
+    var receivedProgressCallback: FileTransferProgressCallback?
+    var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = []
+
+    var env: MessengerEnvironment = .unimplemented
+    env.fileTransfer.get = {
+      var fileTransfer: FileTransfer = .unimplemented
+      fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in
+        receivedProgressCallback = callback
+      }
+      return fileTransfer
+    }
+    let receiveFile: MessengerReceiveFile = .live(env)
+
+    try receiveFile(.stub) { info in
+      didReceiveCallback.append(info)
+    }
+
+    receivedProgressCallback?.handle(.failure(error))
+
+    XCTAssertNoDifference(didReceiveCallback, [
+      .failed(.callbackError(error))
+    ])
+  }
+
+  func testReceiveFileReceiveError() throws {
+    let error = NSError(domain: "test", code: 123)
+
+    var receivedProgressCallback: FileTransferProgressCallback?
+    var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = []
+
+    var env: MessengerEnvironment = .unimplemented
+    env.fileTransfer.get = {
+      var fileTransfer: FileTransfer = .unimplemented
+      fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in
+        receivedProgressCallback = callback
+      }
+      fileTransfer.receive.run = { _ in
+        throw error
+      }
+      return fileTransfer
+    }
+    let receiveFile: MessengerReceiveFile = .live(env)
+
+    try receiveFile(.stub) { info in
+      didReceiveCallback.append(info)
+    }
+
+    receivedProgressCallback?.handle(.success(
+      FileTransferProgressCallback.Callback(
+        progress: Progress(
+          completed: true,
+          transmitted: 3,
+          total: 3,
+          error: nil
+        ),
+        partTracker: .unimplemented
+      )
+    ))
+
+    XCTAssertNoDifference(didReceiveCallback, [
+      .failed(.receiveError(error))
+    ])
+  }
+}
+
+private extension MessengerReceiveFile.Params {
+  static let stub = MessengerReceiveFile.Params(
+    transferId: "transfer-id".data(using: .utf8)!,
+    callbackIntervalMS: 123
+  )
 }