diff --git a/Examples/xx-messenger/Sources/AppCore/SendImage/SendImage.swift b/Examples/xx-messenger/Sources/AppCore/SendImage/SendImage.swift
index 724e33baf6992636b5c2ec8477be47aa06415fb1..a457dcd49e11c3e06975a7151a6bd8a9dd839412 100644
--- a/Examples/xx-messenger/Sources/AppCore/SendImage/SendImage.swift
+++ b/Examples/xx-messenger/Sources/AppCore/SendImage/SendImage.swift
@@ -5,6 +5,14 @@ import XXMessengerClient
 import XXModels
 
 public struct SendImage {
+  public struct ProgressError: Error, Equatable {
+    public init(message: String) {
+      self.message = message
+    }
+
+    public var message: String
+  }
+
   public typealias OnError = (Error) -> Void
   public typealias Completion = () -> Void
 
@@ -27,8 +35,79 @@ extension SendImage {
     now: @escaping () -> Date
   ) -> SendImage {
     SendImage { image, recipientId, onError, completion in
-      // TODO: implement sending image
-      completion()
+      func updateProgress(transferId: Data, progress: Float) {
+        do {
+          if var transfer = try db().fetchFileTransfers(.init(id: [transferId])).first {
+            transfer.progress = progress
+            try db().saveFileTransfer(transfer)
+          }
+        } catch {
+          onError(error)
+        }
+      }
+
+      let file = FileSend(
+        name: "image.jpg",
+        type: "image",
+        preview: nil,
+        contents: image
+      )
+      let params = MessengerSendFile.Params(
+        file: file,
+        recipientId: recipientId,
+        retry: 2,
+        callbackIntervalMS: 500
+      )
+      do {
+        let date = now()
+        let myContactId = try messenger.e2e.tryGet().getContact().getId()
+        let transferId = try messenger.sendFile(params) { info in
+          switch info {
+          case .progress(let transferId, let transmitted, let total):
+            updateProgress(
+              transferId: transferId,
+              progress: total > 0 ? Float(transmitted) / Float(total) : 0
+            )
+
+          case .finished(let transferId):
+            updateProgress(
+              transferId: transferId,
+              progress: 1
+            )
+
+          case .failed(_, .error(let error)):
+            onError(error)
+
+          case .failed(_, .progressError(let message)):
+            onError(ProgressError(message: message))
+
+          case .failed(_, .close(let error)):
+            onError(error)
+          }
+        }
+        try db().saveFileTransfer(XXModels.FileTransfer(
+          id: transferId,
+          contactId: myContactId,
+          name: file.name,
+          type: file.type,
+          data: image,
+          progress: 0,
+          isIncoming: false,
+          createdAt: date
+        ))
+        try db().saveMessage(XXModels.Message(
+          senderId: myContactId,
+          recipientId: recipientId,
+          groupId: nil,
+          date: date,
+          status: .sent,
+          isUnread: false,
+          text: "",
+          fileTransferId: transferId
+        ))
+      } catch {
+        onError(error)
+      }
     }
   }
 }
diff --git a/Examples/xx-messenger/Tests/AppCoreTests/SendImage/SendImageTests.swift b/Examples/xx-messenger/Tests/AppCoreTests/SendImage/SendImageTests.swift
index 147b63aca5193d58b5966e88edf59e149aa6ff88..d1cb1741abe2ffdbd2f4481800ca8529943cb9df 100644
--- a/Examples/xx-messenger/Tests/AppCoreTests/SendImage/SendImageTests.swift
+++ b/Examples/xx-messenger/Tests/AppCoreTests/SendImage/SendImageTests.swift
@@ -9,13 +9,46 @@ final class SendImageTests: XCTestCase {
   func testSend() {
     let image = "image-data".data(using: .utf8)!
     let recipientId = "recipient-id".data(using: .utf8)!
+    let myContactId = "my-contact-id".data(using: .utf8)!
+    let transferId = "transfer-id".data(using: .utf8)!
+    let currentDate = Date(timeIntervalSince1970: 123)
 
     var actions: [Action] = []
+    var sendFileCallback: MessengerSendFile.Callback?
 
-    let messenger: Messenger = .unimplemented
-    let db: DBManagerGetDB = .unimplemented
-    let now: () -> Date = Date.init
-    let send: SendImage = .live(messenger: messenger, db: db, now: now)
+    var messenger: Messenger = .unimplemented
+    messenger.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.getContact.run = {
+        var contact: XXClient.Contact = .unimplemented(Data())
+        contact.getIdFromContact.run = { _ in myContactId }
+        return contact
+      }
+      return e2e
+    }
+    messenger.sendFile.run = { params, callback in
+      actions.append(.didSendFile(params))
+      sendFileCallback = callback
+      return transferId
+    }
+    var db: DBManagerGetDB = .unimplemented
+    db.run = {
+      var db: Database = .unimplemented
+      db.saveFileTransfer.run = { model in
+        actions.append(.didSaveFileTransfer(model))
+        return model
+      }
+      db.saveMessage.run = { model in
+        actions.append(.didSaveMessage(model))
+        return model
+      }
+      db.fetchFileTransfers.run = { query in
+        actions.append(.didFetchFileTransfers(query))
+        return [.stub(withProgress: 0)]
+      }
+      return db
+    }
+    let send: SendImage = .live(messenger: messenger, db: db, now: { currentDate })
 
     actions = []
     send(
@@ -30,7 +63,77 @@ final class SendImageTests: XCTestCase {
     )
 
     XCTAssertNoDifference(actions, [
-      .didComplete
+      .didSendFile(.init(
+        file: .init(
+          name: "image.jpg",
+          type: "image",
+          preview: nil,
+          contents: image
+        ),
+        recipientId: recipientId,
+        retry: 2,
+        callbackIntervalMS: 500
+      )),
+      .didSaveFileTransfer(.init(
+        id: transferId,
+        contactId: myContactId,
+        name: "image.jpg",
+        type: "image",
+        data: image,
+        progress: 0,
+        isIncoming: false,
+        createdAt: currentDate
+      )),
+      .didSaveMessage(.init(
+        senderId: myContactId,
+        recipientId: recipientId,
+        groupId: nil,
+        date: currentDate,
+        status: .sent,
+        isUnread: false,
+        text: "",
+        fileTransferId: transferId
+      )),
+    ])
+
+    actions = []
+    let sendError = NSError(domain: "send-error", code: 1)
+    sendFileCallback?(.failed(id: transferId, .error(sendError)))
+
+    XCTAssertNoDifference(actions, [
+      .didFail(sendError),
+    ])
+
+    actions = []
+    let closeError = NSError(domain: "close-error", code: 2)
+    sendFileCallback?(.failed(id: transferId, .close(closeError)))
+
+    XCTAssertNoDifference(actions, [
+      .didFail(closeError),
+    ])
+
+    actions = []
+    let progressError = "progress-error"
+    sendFileCallback?(.failed(id: transferId, .progressError(progressError)))
+
+    XCTAssertNoDifference(actions, [
+      .didFail(SendImage.ProgressError(message: progressError) as NSError),
+    ])
+
+    actions = []
+    sendFileCallback?(.progress(id: transferId, transmitted: 1, total: 2))
+
+    XCTAssertNoDifference(actions, [
+      .didFetchFileTransfers(.init(id: [transferId])),
+      .didSaveFileTransfer(.stub(withProgress: 0.5)),
+    ])
+
+    actions = []
+    sendFileCallback?(.finished(id: transferId))
+
+    XCTAssertNoDifference(actions, [
+      .didFetchFileTransfers(.init(id: [transferId])),
+      .didSaveFileTransfer(.stub(withProgress: 1)),
     ])
   }
 }
@@ -38,4 +141,23 @@ final class SendImageTests: XCTestCase {
 private enum Action: Equatable {
   case didFail(NSError)
   case didComplete
+  case didSendFile(MessengerSendFile.Params)
+  case didSaveFileTransfer(XXModels.FileTransfer)
+  case didSaveMessage(XXModels.Message)
+  case didFetchFileTransfers(XXModels.FileTransfer.Query)
+}
+
+private extension XXModels.FileTransfer {
+  static func stub(withProgress progress: Float) -> XXModels.FileTransfer {
+    XXModels.FileTransfer(
+      id: Data(),
+      contactId: Data(),
+      name: "",
+      type: "",
+      data: nil,
+      progress: progress,
+      isIncoming: false,
+      createdAt: Date(timeIntervalSince1970: 0)
+    )
+  }
 }