diff --git a/src/data_transfer.cpp b/src/data_transfer.cpp
index a67cf40a258e1479015c84a5407bedded41e0a50..49773e45a72303176d993eaf2f7e058fb832e0cc 100644
--- a/src/data_transfer.cpp
+++ b/src/data_transfer.cpp
@@ -20,14 +20,15 @@
 
 #include "data_transfer.h"
 
-#include "manager.h"
+#include "base64.h"
+#include "client/ring_signal.h"
+#include "fileutils.h"
 #include "jamidht/jamiaccount.h"
+#include "jamidht/p2p.h"
+#include "manager.h"
+#include "map_utils.h"
 #include "peer_connection.h"
-#include "fileutils.h"
 #include "string_utils.h"
-#include "map_utils.h"
-#include "client/ring_signal.h"
-#include "jamidht/p2p.h"
 
 #include <thread>
 #include <stdexcept>
@@ -354,13 +355,14 @@ private:
                     onRecvCb_(std::string_view(buf.data(), buf.size()));
             }
             JAMI_DBG() << "FTP#" << getId() << ": sent " << info_.bytesProgress << " bytes";
-            if (internalCompletionCb_)
-                internalCompletionCb_(info_.path);
 
             if (info_.bytesProgress != info_.totalSize)
                 emit(DRing::DataTransferEventCode::closed_by_peer);
-            else
+            else {
+                if (internalCompletionCb_)
+                    internalCompletionCb_(info_.path);
                 emit(DRing::DataTransferEventCode::finished);
+            }
         });
     }
 
@@ -709,7 +711,7 @@ FileInfo::emit(DRing::DataTransferEventCode code)
 {
     if (finishedCb_ && code >= DRing::DataTransferEventCode::finished)
         finishedCb_(uint32_t(code));
-    if (fileId_ != "profile.vcf") {
+    if (interactionId_ != "") {
         // Else it's an internal transfer
         runOnMainThread([info = info_, iid = interactionId_, fid = fileId_, code]() {
             emitSignal<DRing::DataTransferSignal::DataTransferEvent>(info.accountId,
@@ -882,6 +884,8 @@ public:
             fileutils::check_dir(conversationDataPath_.c_str());
             waitingPath_ = conversationDataPath_ + DIR_SEPARATOR_STR + "waiting";
         }
+        profilesPath_ = fileutils::get_data_dir() + DIR_SEPARATOR_STR + accountId_
+                        + DIR_SEPARATOR_STR + "profiles";
         loadWaiting();
     }
 
@@ -918,6 +922,7 @@ public:
     std::string accountId_ {};
     std::string to_ {};
     std::string waitingPath_ {};
+    std::string profilesPath_ {};
     std::string conversationDataPath_ {};
 
     // Pre swarm
@@ -928,7 +933,7 @@ public:
     std::map<std::string, WaitingRequest> waitingIds_ {};
     std::map<std::shared_ptr<ChannelSocket>, std::shared_ptr<OutgoingFile>> outgoings_ {};
     std::map<std::string, std::shared_ptr<IncomingFile>> incomings_ {};
-    std::map<std::string, std::shared_ptr<IncomingFile>> vcards_ {};
+    std::map<std::pair<std::string, std::string>, std::shared_ptr<IncomingFile>> vcards_ {};
 };
 
 TransferManager::TransferManager(const std::string& accountId, const std::string& to)
@@ -1272,34 +1277,48 @@ TransferManager::onIncomingProfile(const std::shared_ptr<ChannelSocket>& channel
 {
     if (!channel)
         return;
+
+    auto name = channel->name();
+    auto lastSep = name.find_last_of('/');
+    auto fileId = name.substr(lastSep + 1);
+
     auto deviceId = channel->deviceId().toString();
     auto cert = channel->peerCertificate();
-    if (!cert || !cert->issuer)
+    if (!cert || !cert->issuer || fileId.find(".vcf") == std::string::npos)
         return;
+
+    auto uri = fileId == "profile.vcf" ? cert->issuer->getId().toString()
+                                       : fileId.substr(0, fileId.size() - 4 /*.vcf*/);
+
     std::lock_guard<std::mutex> lk(pimpl_->mapMutex_);
+    auto idx = std::pair<std::string, std::string> {deviceId, uri};
     // Check if not already an incoming file for this id and that we are waiting this file
-    auto itV = pimpl_->vcards_.find(deviceId);
+    auto itV = pimpl_->vcards_.find(idx);
     if (itV != pimpl_->vcards_.end()) {
         channel->shutdown();
         return;
     }
 
+    auto tid = generateUID();
     DRing::DataTransferInfo info;
     info.accountId = pimpl_->accountId_;
     info.conversationId = pimpl_->to_;
     info.path = fileutils::get_cache_dir() + DIR_SEPARATOR_STR + pimpl_->accountId_
-                + DIR_SEPARATOR_STR + "vcard" + DIR_SEPARATOR_STR + deviceId;
+                + DIR_SEPARATOR_STR + "vcard" + DIR_SEPARATOR_STR + deviceId + "_" + uri + "_"
+                + std::to_string(tid);
 
     auto ifile = std::make_shared<IncomingFile>(std::move(channel), info, "profile.vcf", "");
-    auto res = pimpl_->vcards_.emplace(deviceId, std::move(ifile));
+    auto res = pimpl_->vcards_.emplace(idx, std::move(ifile));
     if (res.second) {
         res.first->second->onFinished([w = weak(),
+                                       uri = std::move(uri),
                                        deviceId = std::move(deviceId),
                                        accountId = pimpl_->accountId_,
                                        cert = std::move(cert),
                                        path = info.path](uint32_t code) {
             // schedule destroy transfer as not needed
             dht::ThreadPool().computation().run([w,
+                                                 uri = std::move(uri),
                                                  deviceId = std::move(deviceId),
                                                  accountId = std::move(accountId),
                                                  cert = std::move(cert),
@@ -1308,13 +1327,12 @@ TransferManager::onIncomingProfile(const std::shared_ptr<ChannelSocket>& channel
                 if (auto sthis_ = w.lock()) {
                     auto& pimpl = sthis_->pimpl_;
                     std::lock_guard<std::mutex> lk {pimpl->mapMutex_};
-                    auto itO = pimpl->vcards_.find(deviceId);
+                    auto itO = pimpl->vcards_.find({deviceId, uri});
                     if (itO != pimpl->vcards_.end())
                         pimpl->vcards_.erase(itO);
                     if (code == uint32_t(DRing::DataTransferEventCode::finished))
                         emitSignal<DRing::ConfigurationSignal::ProfileReceived>(accountId,
-                                                                                cert->issuer->getId()
-                                                                                    .toString(),
+                                                                                uri,
                                                                                 path);
                 }
             });
@@ -1323,6 +1341,13 @@ TransferManager::onIncomingProfile(const std::shared_ptr<ChannelSocket>& channel
     }
 }
 
+std::string
+TransferManager::profilePath(const std::string& contactId) const
+{
+    // TODO Android? iOS?
+    return pimpl_->profilesPath_ + DIR_SEPARATOR_STR + base64::encode(contactId) + ".vcf";
+}
+
 std::vector<WaitingRequest>
 TransferManager::waitingRequests() const
 {
diff --git a/src/data_transfer.h b/src/data_transfer.h
index b41b2d7951271103a83ec35eef4325bb0b113771..24041aac6e21d08710132edddfde7ef19e17f47e 100644
--- a/src/data_transfer.h
+++ b/src/data_transfer.h
@@ -235,6 +235,12 @@ public:
     bool isWaiting(const std::string& fileId) const;
     void onIncomingProfile(const std::shared_ptr<ChannelSocket>& channel);
 
+    /**
+     * @param contactId     contact's id
+     * @return where profile.vcf is stored
+     */
+    std::string profilePath(const std::string& contactId) const;
+
 private:
     std::weak_ptr<TransferManager> weak()
     {
diff --git a/src/jamidht/CMakeLists.txt b/src/jamidht/CMakeLists.txt
index eb2c0acca0ac7f8f04d26ea80e350cb1beb8c2a2..b2b7812db0cffacfb38781ec45d83060b3fdb074 100644
--- a/src/jamidht/CMakeLists.txt
+++ b/src/jamidht/CMakeLists.txt
@@ -35,6 +35,8 @@ list (APPEND Source_Files__jamidht
       "${CMAKE_CURRENT_SOURCE_DIR}/channel_handler.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/conversation_channel_handler.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/conversation_channel_handler.cpp"
+      "${CMAKE_CURRENT_SOURCE_DIR}/transfer_channel_handler.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/transfer_channel_handler.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/conversation_module.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/conversation_module.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/namedirectory.cpp"
diff --git a/src/jamidht/Makefile.am b/src/jamidht/Makefile.am
index 67f74ceb46d83ac2311b7105e3985d114edc4e33..4ab94e574a909e4f0abdfa8e3a6eae8af10ec127 100644
--- a/src/jamidht/Makefile.am
+++ b/src/jamidht/Makefile.am
@@ -43,7 +43,9 @@ libringacc_la_SOURCES = \
 	./jamidht/sync_channel_handler.h \
 	./jamidht/sync_channel_handler.cpp \
 	./jamidht/sync_module.h \
-	./jamidht/sync_module.cpp
+	./jamidht/sync_module.cpp \
+	./jamidht/transfer_channel_handler.h \
+	./jamidht/transfer_channel_handler.cpp
 
 if RINGNS
 libringacc_la_SOURCES += \
diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp
index 63db5e8cbf0a46c7abe3c618c06fdad583f61935..c6686fbd60dbd0d8dca227480e7cdf826c8ec794 100644
--- a/src/jamidht/conversation.cpp
+++ b/src/jamidht/conversation.cpp
@@ -1091,7 +1091,7 @@ Conversation::downloadFile(const std::string& interactionId,
                                                         sha3sum,
                                                         path,
                                                         totalSize);
-                acc->askForFileChannel(shared->id(), deviceId, fileId, start, end);
+                acc->askForFileChannel(shared->id(), deviceId, interactionId, fileId, start, end);
             }
         });
     return true;
diff --git a/src/jamidht/conversation_channel_handler.cpp b/src/jamidht/conversation_channel_handler.cpp
index 4d7edbd081ed61fcbfc2eab6aff4ccfb9bc3cd17..ef8c37e09772c027bba64dc028340a9299a38aae 100644
--- a/src/jamidht/conversation_channel_handler.cpp
+++ b/src/jamidht/conversation_channel_handler.cpp
@@ -22,7 +22,7 @@
 
 namespace jami {
 
-ConversationChannelHandler::ConversationChannelHandler(std::weak_ptr<JamiAccount>&& acc,
+ConversationChannelHandler::ConversationChannelHandler(const std::shared_ptr<JamiAccount>& acc,
                                                        ConnectionManager& cm)
     : ChannelHandlerInterface()
     , account_(acc)
diff --git a/src/jamidht/conversation_channel_handler.h b/src/jamidht/conversation_channel_handler.h
index 455bc0790a8d760b8786ec00fe0f6dbca5459104..b9418fb8e82d610d6aa8dfab87af3b309e2b5885 100644
--- a/src/jamidht/conversation_channel_handler.h
+++ b/src/jamidht/conversation_channel_handler.h
@@ -32,7 +32,7 @@ namespace jami {
 class ConversationChannelHandler : public ChannelHandlerInterface
 {
 public:
-    ConversationChannelHandler(std::weak_ptr<JamiAccount>&& acc, ConnectionManager& cm);
+    ConversationChannelHandler(const std::shared_ptr<JamiAccount>& acc, ConnectionManager& cm);
     ~ConversationChannelHandler();
 
     /**
diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp
index e6884c56a213c79d535daa177958cea921686b85..131dfe762e777d098a0c85ff5d507a9ab88232fe 100644
--- a/src/jamidht/conversation_module.cpp
+++ b/src/jamidht/conversation_module.cpp
@@ -485,6 +485,20 @@ ConversationModule::Impl::handlePendingConversations()
                                                                         *commits.rbegin(),
                                                                         true);
                                 });
+                        // Download members profile on first sync
+                        if (auto cert = tls::CertificateStore::instance().getCertificate(deviceId)) {
+                            if (cert->issuer
+                                && cert->issuer->getId().toString() == sthis->username_) {
+                                if (auto acc = sthis->account_.lock()) {
+                                    for (const auto& member : conversation->getMembers()) {
+                                        if (member.at("uri") != sthis->username_)
+                                            acc->askForProfile(conversationId,
+                                                               deviceId,
+                                                               member.at("uri"));
+                                    }
+                                }
+                            }
+                        }
                     }
                 } catch (const std::exception& e) {
                     emitSignal<DRing::ConversationSignal::OnConversationError>(sthis->accountId_,
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index f1930379e7257aff0148b5dede02553952a137c8..32a53d2b7436fe47a94d7ec1ddc6a713d6b74852 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -40,6 +40,7 @@
 #include "multiplexed_socket.h"
 #include "conversation_channel_handler.h"
 #include "sync_channel_handler.h"
+#include "transfer_channel_handler.h"
 
 #include "sip/sdp.h"
 #include "sip/sipvoiplink.h"
@@ -2192,38 +2193,6 @@ JamiAccount::doRegister_()
                     std::lock_guard<std::mutex> lk(transfersMtx_);
                     incomingFileTransfers_.emplace(tid);
                     return true;
-                } else if (isDataTransfer) {
-                    // Check if sync request is from same account
-                    std::promise<bool> accept;
-                    std::future<bool> fut = accept.get_future();
-
-                    auto idstr = name.substr(16);
-                    auto sep = idstr.find('/');
-                    auto lastSep = idstr.find_last_of('/');
-                    auto conversationId = idstr.substr(0, sep);
-                    auto fileHost = idstr.substr(sep + 1, lastSep - sep - 1);
-                    auto fileId = idstr.substr(lastSep + 1);
-                    if (fileHost == currentDeviceId())
-                        return false;
-
-                    sep = fileId.find_last_of('?');
-                    if (sep != std::string::npos) {
-                        fileId = fileId.substr(0, sep);
-                    }
-
-                    // Check if peer is member of the conversation
-                    if (fileId == "profile.vcf") {
-                        auto members = convModule()->getConversationMembers(conversationId);
-                        return std::find_if(members.begin(),
-                                            members.end(),
-                                            [&](auto m) { return m["uri"] == issuer; })
-                               != members.end();
-                    }
-
-                    return convModule()->onFileChannelRequest(conversationId,
-                                                              issuer,
-                                                              fileId,
-                                                              !noSha3sumVerification_);
                 }
                 return false;
             });
@@ -2248,7 +2217,7 @@ JamiAccount::doRegister_()
                         return;
                     incomingFileTransfers_.erase(it);
                     lk.unlock();
-                    std::function<void(const std::string&)> cb;
+                    InternalCompletionCb cb;
                     if (isVCard)
                         cb = [peerId, accountId = getAccountID()](const std::string& path) {
                             emitSignal<DRing::ConfigurationSignal::ProfileReceived>(accountId,
@@ -2323,53 +2292,6 @@ JamiAccount::doRegister_()
                             shared->gitServers_.erase(serverId);
                         });
                     });
-                } else if (name.find(DATA_TRANSFER_URI) == 0) {
-                    auto idstr = name.substr(16);
-                    auto sep = idstr.find('/');
-                    auto lastSep = idstr.find_last_of('/');
-                    auto conversationId = idstr.substr(0, sep);
-                    auto fileId = idstr.substr(lastSep + 1);
-                    if (channel->isInitiator())
-                        return;
-
-                    sep = fileId.find_last_of('?');
-                    std::string arguments;
-                    if (sep != std::string::npos) {
-                        arguments = fileId.substr(sep + 1);
-                        fileId = fileId.substr(0, sep);
-                    }
-
-                    if (fileId == "profile.vcf") {
-                        std::string path = fileutils::sha3File(idPath_ + DIR_SEPARATOR_STR
-                                                               + "profile.vcf");
-                        dataTransfer()->transferFile(channel, fileId, "", path);
-                        return;
-                    }
-                    auto dt = dataTransfer(conversationId);
-                    sep = fileId.find('_');
-                    if (!dt or sep == std::string::npos) {
-                        channel->shutdown();
-                        return;
-                    }
-                    auto interactionId = fileId.substr(0, sep);
-                    std::string path = dt->path(fileId);
-                    auto start = 0u, end = 0u;
-                    for (const auto arg : jami::split_string(arguments, '&')) {
-                        auto keyVal = jami::split_string(arg, '=');
-                        if (keyVal.size() == 2) {
-                            if (keyVal[0] == "start") {
-                                std::from_chars(keyVal[1].data(),
-                                                keyVal[1].data() + keyVal[1].size(),
-                                                start);
-                            } else if (keyVal[0] == "end") {
-                                std::from_chars(keyVal[1].data(),
-                                                keyVal[1].data() + keyVal[1].size(),
-                                                end);
-                            }
-                        }
-                    }
-
-                    dt->transferFile(channel, fileId, interactionId, path, start, end);
                 } else {
                     // TODO move git://
                     auto uri = Uri(name);
@@ -4172,12 +4094,16 @@ JamiAccount::sendProfile(const std::string& deviceId)
 
         sendFile(deviceId,
                  idPath_ + DIR_SEPARATOR_STR + "profile.vcf",
-                 [deviceId, accId = getAccountID()](const std::string&) {
+                 [deviceId, this](const std::string&) {
                      // Mark the VCard as sent
-                     auto path = fileutils::get_cache_dir() + DIR_SEPARATOR_STR + accId
+                     auto path = fileutils::get_cache_dir() + DIR_SEPARATOR_STR + getAccountID()
                                  + DIR_SEPARATOR_STR + "vcard" + DIR_SEPARATOR_STR + deviceId;
+                     std::lock_guard<std::mutex> lock(fileutils::getFileLock(path));
+                     if (fileutils::isFile(path))
+                         return;
                      fileutils::ofstream(path);
                  });
+
     } catch (const std::exception& e) {
         JAMI_ERR() << e.what();
     }
@@ -4413,6 +4339,7 @@ JamiAccount::transferFile(const std::string& conversationId,
 void
 JamiAccount::askForFileChannel(const std::string& conversationId,
                                const std::string& deviceId,
+                               const std::string& interactionId,
                                const std::string& fileId,
                                size_t start,
                                size_t end)
@@ -4432,21 +4359,23 @@ JamiAccount::askForFileChannel(const std::string& conversationId,
         connectionManager_->connectDevice(
             did,
             channelName,
-            [this, conversationId, fileId](std::shared_ptr<ChannelSocket> channel, const DeviceId&) {
+            [this, conversationId, fileId, interactionId](std::shared_ptr<ChannelSocket> channel,
+                                                          const DeviceId&) {
                 if (!channel)
                     return;
-                dht::ThreadPool::io().run([w = weak(), conversationId, channel, fileId] {
-                    auto shared = w.lock();
-                    if (!shared)
-                        return;
-                    auto dt = shared->dataTransfer(conversationId);
-                    if (!dt)
-                        return;
-                    if (fileId == "profile.vcf")
-                        dt->onIncomingProfile(channel);
-                    else
-                        dt->onIncomingFileTransfer(fileId, channel);
-                });
+                dht::ThreadPool::io().run(
+                    [w = weak(), conversationId, channel, fileId, interactionId] {
+                        auto shared = w.lock();
+                        if (!shared)
+                            return;
+                        auto dt = shared->dataTransfer(conversationId);
+                        if (!dt)
+                            return;
+                        if (interactionId.empty())
+                            dt->onIncomingProfile(channel);
+                        else
+                            dt->onIncomingFileTransfer(fileId, channel);
+                    });
             },
             false);
     };
@@ -4467,15 +4396,44 @@ JamiAccount::askForFileChannel(const std::string& conversationId,
     }
 }
 
+void
+JamiAccount::askForProfile(const std::string& conversationId,
+                           const std::string& deviceId,
+                           const std::string& memberUri)
+{
+    std::lock_guard<std::mutex> lkCM(connManagerMtx_);
+    if (!connectionManager_)
+        return;
+
+    auto channelName = DATA_TRANSFER_URI + conversationId + "/profile/" + memberUri + ".vcf";
+    // We can avoid to negotiate new sessions, as the file notif
+    // probably come from an online device or last connected device.
+    connectionManager_->connectDevice(
+        DeviceId(deviceId),
+        channelName,
+        [this, conversationId](std::shared_ptr<ChannelSocket> channel, const DeviceId&) {
+            if (!channel)
+                return;
+            dht::ThreadPool::io().run([w = weak(), conversationId, channel] {
+                if (auto shared = w.lock())
+                    if (auto dt = shared->dataTransfer(conversationId))
+                        dt->onIncomingProfile(channel);
+            });
+        },
+        false);
+}
+
 void
 JamiAccount::initConnectionManager()
 {
     if (!connectionManager_) {
         connectionManager_ = std::make_unique<ConnectionManager>(*this);
         channelHandlers_[Uri::Scheme::GIT]
-            = std::make_unique<ConversationChannelHandler>(weak(), *connectionManager_.get());
+            = std::make_unique<ConversationChannelHandler>(shared(), *connectionManager_.get());
         channelHandlers_[Uri::Scheme::SYNC]
-            = std::make_unique<SyncChannelHandler>(weak(), *connectionManager_.get());
+            = std::make_unique<SyncChannelHandler>(shared(), *connectionManager_.get());
+        channelHandlers_[Uri::Scheme::DATA_TRANSFER]
+            = std::make_unique<TransferChannelHandler>(shared(), *connectionManager_.get());
     }
 }
 
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index 0587a2a37afad5a6f5a54b0d1ed50b3896c958d3..3c7ebf391e927c030b082fd19bb4e70ed708bb84 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -560,10 +560,15 @@ public:
 
     void askForFileChannel(const std::string& conversationId,
                            const std::string& deviceId,
+                           const std::string& interactionId,
                            const std::string& fileId,
                            size_t start = 0,
                            size_t end = 0);
 
+    void askForProfile(const std::string& conversationId,
+                       const std::string& deviceId,
+                       const std::string& memberUri);
+
     /**
      * Retrieve linked transfer manager
      * @param id    conversationId or empty for fallback
@@ -581,7 +586,7 @@ public:
     // Note: when swarm will be merged, this can be moved in transferManager
     bool needToSendProfile(const std::string& deviceId);
     /**
-     * Send Profile via cached SIP connection
+     * Send profile via cached SIP connection
      * @param deviceId      Device that will receive the profile
      */
     void sendProfile(const std::string& deviceId);
@@ -590,6 +595,8 @@ public:
 
     AccountManager* accountManager() { return accountManager_.get(); }
 
+    bool sha3SumVerify() const { return !noSha3sumVerification_; }
+
 private:
     NON_COPYABLE(JamiAccount);
 
diff --git a/src/jamidht/multiplexed_socket.cpp b/src/jamidht/multiplexed_socket.cpp
index 5930a1561460a258283711afbb665ec456643f74..3f6121416803cb290931fe4d881a910bf649f03c 100644
--- a/src/jamidht/multiplexed_socket.cpp
+++ b/src/jamidht/multiplexed_socket.cpp
@@ -257,7 +257,9 @@ void
 MultiplexedSocket::Impl::onAccept(const std::string& name, uint16_t channel)
 {
     std::lock_guard<std::mutex> lkSockets(socketsMutex);
-    auto socket = makeSocket(name, channel);
+    auto& socket = sockets[channel];
+    if (!socket)
+        socket = makeSocket(name, channel);
     onChannelReady_(deviceId, socket);
     socket->ready();
 }
diff --git a/src/jamidht/sync_channel_handler.cpp b/src/jamidht/sync_channel_handler.cpp
index 6adb88073a21d45682d03f4f8211d8b54b78cfa0..c75123a09af4dcad73aea5e0a4045559e674c2ae 100644
--- a/src/jamidht/sync_channel_handler.cpp
+++ b/src/jamidht/sync_channel_handler.cpp
@@ -24,7 +24,8 @@ static constexpr const char SYNC_URI[] {"sync://"};
 
 namespace jami {
 
-SyncChannelHandler::SyncChannelHandler(std::weak_ptr<JamiAccount>&& acc, ConnectionManager& cm)
+SyncChannelHandler::SyncChannelHandler(const std::shared_ptr<JamiAccount>& acc,
+                                       ConnectionManager& cm)
     : ChannelHandlerInterface()
     , account_(acc)
     , connectionManager_(cm)
@@ -69,7 +70,6 @@ SyncChannelHandler::onReady(const DeviceId& deviceId,
     if (!cert || !acc || !acc->syncModule())
         return;
     acc->syncModule()->cacheSyncConnection(std::move(channel), cert->getIssuerUID(), deviceId);
-    acc->sendProfile(deviceId.toString());
 }
 
 } // namespace jami
\ No newline at end of file
diff --git a/src/jamidht/sync_channel_handler.h b/src/jamidht/sync_channel_handler.h
index 0cce9403570a99d22e78b244f6ec8612f30dbc63..5e6f72e03af9c8eb2ebe5b513227df1a35ae1dc3 100644
--- a/src/jamidht/sync_channel_handler.h
+++ b/src/jamidht/sync_channel_handler.h
@@ -32,7 +32,7 @@ namespace jami {
 class SyncChannelHandler : public ChannelHandlerInterface
 {
 public:
-    SyncChannelHandler(std::weak_ptr<JamiAccount>&& acc, ConnectionManager& cm);
+    SyncChannelHandler(const std::shared_ptr<JamiAccount>& acc, ConnectionManager& cm);
     ~SyncChannelHandler();
 
     /**
diff --git a/src/jamidht/transfer_channel_handler.cpp b/src/jamidht/transfer_channel_handler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..824f308ef43364876a3137730a673daf075f2f80
--- /dev/null
+++ b/src/jamidht/transfer_channel_handler.cpp
@@ -0,0 +1,149 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include "jamidht/transfer_channel_handler.h"
+
+#include <charconv>
+
+#include "fileutils.h"
+
+namespace jami {
+
+TransferChannelHandler::TransferChannelHandler(const std::shared_ptr<JamiAccount>& account,
+                                               ConnectionManager& cm)
+    : ChannelHandlerInterface()
+    , account_(account)
+    , connectionManager_(cm)
+{
+    auto acc = account_.lock();
+    idPath_ = fileutils::get_data_dir() + DIR_SEPARATOR_STR + acc->getAccountID();
+}
+
+TransferChannelHandler::~TransferChannelHandler() {}
+
+void
+TransferChannelHandler::connect(const DeviceId& deviceId,
+                                const std::string& channelName,
+                                ConnectCb&& cb)
+{}
+
+bool
+TransferChannelHandler::onRequest(const DeviceId& deviceId, const std::string& name)
+{
+    auto acc = account_.lock();
+    auto cert = tls::CertificateStore::instance().getCertificate(deviceId.toString());
+    if (!acc || !cert || !cert->issuer)
+        return false;
+    auto uri = cert->issuer->getId().toString();
+    auto idstr = name.substr(16);
+    auto sep = idstr.find('/');
+    auto lastSep = idstr.find_last_of('/');
+    auto conversationId = idstr.substr(0, sep);
+    auto fileHost = idstr.substr(sep + 1, lastSep - sep - 1);
+    auto fileId = idstr.substr(lastSep + 1);
+    if (fileHost == acc->currentDeviceId())
+        return false;
+
+    sep = fileId.find_last_of('?');
+    if (sep != std::string::npos) {
+        fileId = fileId.substr(0, sep);
+    }
+
+    // Check if peer is member of the conversation
+    if (fileId == "profile.vcf") {
+        auto members = acc->convModule()->getConversationMembers(conversationId);
+        return std::find_if(members.begin(), members.end(), [&](auto m) { return m["uri"] == uri; })
+               != members.end();
+    } else if (fileHost == "profile") {
+        // If a profile is sent, check if it's from another device
+        return uri == acc->getUsername();
+    }
+
+    return acc->convModule()->onFileChannelRequest(conversationId,
+                                                   uri,
+                                                   fileId,
+                                                   acc->sha3SumVerify());
+}
+
+void
+TransferChannelHandler::onReady(const DeviceId&,
+                                const std::string& name,
+                                std::shared_ptr<ChannelSocket> channel)
+{
+    auto acc = account_.lock();
+    if (!acc)
+        return;
+
+    auto idstr = name.substr(16);
+    auto splitted_id = split_string(idstr, '/');
+    if (splitted_id.size() < 3) {
+        JAMI_ERR() << "Unsupported ID detected " << name;
+        channel->shutdown();
+        return;
+    }
+
+    // convId/fileHost/fileId or convId/profile/fileId
+    auto conversationId = std::string(splitted_id[0]);
+    auto fileHost = std::string(splitted_id[1]);
+    auto isContactProfile = splitted_id[1] == "profile";
+    auto fileId = std::string(splitted_id[splitted_id.size() - 1]);
+    if (channel->isInitiator())
+        return;
+
+    auto sep = fileId.find_last_of('?');
+    std::string arguments;
+    if (sep != std::string::npos) {
+        arguments = fileId.substr(sep + 1);
+        fileId = fileId.substr(0, sep);
+    }
+
+    if (fileId == "profile.vcf") {
+        std::string path = fileutils::sha3File(idPath_ + DIR_SEPARATOR_STR + "profile.vcf");
+        acc->dataTransfer()->transferFile(channel, fileId, "", path);
+        return;
+    } else if (isContactProfile && fileId.find(".vcf") != std::string::npos) {
+        auto path = acc->dataTransfer()->profilePath(fileId.substr(0, fileId.size() - 4));
+        acc->dataTransfer()->transferFile(channel, fileId, "", path);
+        return;
+    }
+    auto dt = acc->dataTransfer(conversationId);
+    sep = fileId.find('_');
+    if (!dt or sep == std::string::npos) {
+        channel->shutdown();
+        return;
+    }
+    auto interactionId = fileId.substr(0, sep);
+    std::string path = dt->path(fileId);
+    auto start = 0u, end = 0u;
+    for (const auto arg : split_string(arguments, '&')) {
+        auto keyVal = split_string(arg, '=');
+        if (keyVal.size() == 2) {
+            if (keyVal[0] == "start") {
+                std::from_chars(keyVal[1].data(), keyVal[1].data() + keyVal[1].size(), start);
+            } else if (keyVal[0] == "end") {
+                std::from_chars(keyVal[1].data(), keyVal[1].data() + keyVal[1].size(), end);
+            }
+        }
+    }
+
+    dt->transferFile(channel, fileId, interactionId, path, start, end);
+}
+
+} // namespace jami
\ No newline at end of file
diff --git a/src/jamidht/transfer_channel_handler.h b/src/jamidht/transfer_channel_handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..62c777ef896c902d4aeb55fe6ef024825098d5a9
--- /dev/null
+++ b/src/jamidht/transfer_channel_handler.h
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include "jamidht/channel_handler.h"
+#include "jamidht/connectionmanager.h"
+#include "jamidht/jamiaccount.h"
+
+namespace jami {
+
+/**
+ * Manages Conversation's channels
+ */
+class TransferChannelHandler : public ChannelHandlerInterface
+{
+public:
+    TransferChannelHandler(const std::shared_ptr<JamiAccount>& acc, ConnectionManager& cm);
+    ~TransferChannelHandler();
+
+    /**
+     * Ask for a new channel
+     * This replaces the connectDevice() in jamiaccount
+     * @param deviceId      The device to connect
+     * @param channelName   The name of the channel
+     * @param cb            The callback to call when connected (can be immediate if already connected)
+     */
+    void connect(const DeviceId& deviceId, const std::string& channelName, ConnectCb&& cb) override;
+
+    /**
+     * Determine if we accept or not the request
+     * @param deviceId      device who asked
+     * @param name          name asked
+     * @return if we accept or not
+     */
+    bool onRequest(const DeviceId& deviceId, const std::string& name) override;
+
+    /**
+     * Handle socket ready
+     * @param deviceId      Related device
+     * @param name          Name of the handler
+     * @param channel       Channel to handle
+     */
+    void onReady(const DeviceId& deviceId,
+                 const std::string& name,
+                 std::shared_ptr<ChannelSocket> channel) override;
+
+private:
+    std::weak_ptr<JamiAccount> account_;
+    ConnectionManager& connectionManager_;
+
+    std::string idPath_;
+};
+
+} // namespace jami
\ No newline at end of file
diff --git a/src/uri.cpp b/src/uri.cpp
index c6499e7c49265bbe7a689bebb8376e787fe74bef..4b5024759cab31630bf1eddde1bb402a9022a397 100644
--- a/src/uri.cpp
+++ b/src/uri.cpp
@@ -35,6 +35,8 @@ Uri::Uri(const std::string_view& uri)
             scheme_ = Uri::Scheme::SWARM;
         else if (scheme_str == "jami")
             scheme_ = Uri::Scheme::JAMI;
+        else if (scheme_str == "data-transfer")
+            scheme_ = Uri::Scheme::DATA_TRANSFER;
         else if (scheme_str == "git")
             scheme_ = Uri::Scheme::GIT;
         else if (scheme_str == "sync")
diff --git a/src/uri.h b/src/uri.h
index 9dfcf945778c43dceb8f0271437b5c4288375362..4db5d9ba30b061dcb299215e1be702bf6ff880dc 100644
--- a/src/uri.h
+++ b/src/uri.h
@@ -28,12 +28,13 @@ class Uri
 {
 public:
     enum class Scheme {
-        JAMI,        // Start with "jami:" and 45 ASCII chars OR 40 ASCII chars
-        SIP,         // Start with "sip:"
-        SWARM,       // Start with "swarm:" and 40 ASCII chars
-        GIT,         // Start with "git:"
-        SYNC,        // Start with "sync:"
-        UNRECOGNIZED // Anything that doesn't fit in other categories
+        JAMI,          // Start with "jami:" and 45 ASCII chars OR 40 ASCII chars
+        SIP,           // Start with "sip:"
+        SWARM,         // Start with "swarm:" and 40 ASCII chars
+        GIT,           // Start with "git:"
+        DATA_TRANSFER, // Start with "data-transfer://"
+        SYNC,          // Start with "sync:"
+        UNRECOGNIZED   // Anything that doesn't fit in other categories
     };
 
     Uri(const std::string_view& uri);
diff --git a/test/unitTest/fileTransfer/fileTransfer.cpp b/test/unitTest/fileTransfer/fileTransfer.cpp
index 25bbca243fe2faf8560156f1ecc2c7fec51f0eff..0e15cebaabaad3105de6b4c4511695654ec12e7f 100644
--- a/test/unitTest/fileTransfer/fileTransfer.cpp
+++ b/test/unitTest/fileTransfer/fileTransfer.cpp
@@ -70,6 +70,7 @@ private:
     void testMultipleFileTransfer();
     void testConversationFileTransfer();
     void testFileTransferInConversation();
+    void testVcfFileTransferInConversation();
     void testBadSha3sumOut();
     void testBadSha3sumIn();
     void testAskToMultipleParticipants();
@@ -83,6 +84,7 @@ private:
     CPPUNIT_TEST(testMultipleFileTransfer);
     CPPUNIT_TEST(testConversationFileTransfer);
     CPPUNIT_TEST(testFileTransferInConversation);
+    CPPUNIT_TEST(testVcfFileTransferInConversation);
     CPPUNIT_TEST(testBadSha3sumOut);
     CPPUNIT_TEST(testBadSha3sumIn);
     CPPUNIT_TEST(testAskToMultipleParticipants);
@@ -162,7 +164,9 @@ FileTransferTest::testFileTransfer()
     DRing::registerSignalHandlers(confHandlers);
 
     // Create file to send
-    std::ofstream sendFile(sendPath);
+    auto sendVcfPath = sendPath + ".vcf";
+    auto recvVcfPath = recvPath + ".vcf";
+    std::ofstream sendFile(sendVcfPath);
     CPPUNIT_ASSERT(sendFile.is_open());
     sendFile << std::string(64000, 'A');
     sendFile.close();
@@ -172,15 +176,15 @@ FileTransferTest::testFileTransfer()
     uint64_t id;
     info.accountId = aliceAccount->getAccountID();
     info.peer = bobUri;
-    info.path = sendPath;
-    info.displayName = "SEND";
+    info.path = sendVcfPath;
+    info.displayName = "SEND.vcf";
     info.bytesProgress = 0;
     CPPUNIT_ASSERT(DRing::sendFileLegacy(info, id) == DRing::DataTransferError::success);
 
     cv.wait_for(lk, std::chrono::seconds(30));
     CPPUNIT_ASSERT(transferWaiting);
 
-    CPPUNIT_ASSERT(DRing::acceptFileTransfer(bobId, finalId, recvPath)
+    CPPUNIT_ASSERT(DRing::acceptFileTransfer(bobId, finalId, recvVcfPath)
                    == DRing::DataTransferError::success);
 
     // Wait 2 times, both sides will got a finished status
@@ -188,14 +192,12 @@ FileTransferTest::testFileTransfer()
     cv.wait_for(lk, std::chrono::seconds(30));
     CPPUNIT_ASSERT(transferFinished);
 
-    CPPUNIT_ASSERT(compare(info.path, recvPath));
+    CPPUNIT_ASSERT(compare(info.path, recvVcfPath));
 
     // TODO FIX ME. The ICE take some time to stop and it doesn't seems to like
     // when stopping the daemon and removing the accounts to soon.
-    std::remove(sendPath.c_str());
-    std::remove(recvPath.c_str());
-    JAMI_INFO("Waiting....");
-    std::this_thread::sleep_for(std::chrono::seconds(3));
+    std::remove(sendVcfPath.c_str());
+    std::remove(recvVcfPath.c_str());
 }
 
 void
@@ -288,8 +290,6 @@ FileTransferTest::testDataTransferInfo()
     // when stopping the daemon and removing the accounts to soon.
     std::remove(sendPath.c_str());
     std::remove(recvPath.c_str());
-    JAMI_INFO("Waiting....");
-    std::this_thread::sleep_for(std::chrono::seconds(3));
 }
 
 void
@@ -391,8 +391,6 @@ FileTransferTest::testMultipleFileTransfer()
     std::remove(sendPath2.c_str());
     std::remove(recvPath.c_str());
     std::remove(recv2Path.c_str());
-    JAMI_INFO("Waiting....");
-    std::this_thread::sleep_for(std::chrono::seconds(3));
 }
 
 void
@@ -611,6 +609,103 @@ FileTransferTest::testFileTransferInConversation()
     std::this_thread::sleep_for(std::chrono::seconds(5));
 }
 
+void
+FileTransferTest::testVcfFileTransferInConversation()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobUri = bobAccount->getUsername();
+    auto aliceUri = aliceAccount->getUsername();
+    auto convId = DRing::startConversation(aliceId);
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
+    bool requestReceived = false;
+    bool conversationReady = false;
+    bool bobJoined = false;
+    std::string tidBob, iidBob;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>(
+        [&](const std::string& accountId,
+            const std::string& /* conversationId */,
+            std::map<std::string, std::string> message) {
+            if (message["type"] == "application/data-transfer+json") {
+                if (accountId == bobId) {
+                    tidBob = message["fileId"];
+                    iidBob = message["id"];
+                }
+            }
+            if (accountId == aliceId && message["type"] == "member" && message["action"] == "join") {
+                bobJoined = true;
+            }
+            cv.notify_one();
+        }));
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>(
+            [&](const std::string& /*accountId*/,
+                const std::string& /* conversationId */,
+                std::map<std::string, std::string> /*metadatas*/) {
+                requestReceived = true;
+                cv.notify_one();
+            }));
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>(
+        [&](const std::string& accountId, const std::string& /* conversationId */) {
+            if (accountId == bobId) {
+                conversationReady = true;
+                cv.notify_one();
+            }
+        }));
+    bool transferAFinished = false, transferBFinished = false;
+    // Watch signals
+    confHandlers.insert(DRing::exportable_callback<DRing::DataTransferSignal::DataTransferEvent>(
+        [&](const std::string& accountId,
+            const std::string& conversationId,
+            const std::string&,
+            const std::string&,
+            int code) {
+            if (code == static_cast<int>(DRing::DataTransferEventCode::finished)
+                && conversationId == convId) {
+                if (accountId == aliceId)
+                    transferAFinished = true;
+                else if (accountId == bobId)
+                    transferBFinished = true;
+            }
+            cv.notify_one();
+        }));
+    DRing::registerSignalHandlers(confHandlers);
+
+    DRing::addConversationMember(aliceId, convId, bobUri);
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+
+    DRing::acceptConversationRequest(bobId, convId);
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
+        return conversationReady && bobJoined;
+    }));
+
+    // Create file to send
+    std::ofstream sendFile(sendPath);
+    CPPUNIT_ASSERT(sendFile.is_open());
+    sendFile << std::string(64000, 'A');
+    sendFile.close();
+
+    DRing::sendFile(aliceId, convId, sendPath, "SEND", "");
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return !tidBob.empty(); }));
+
+    transferAFinished = false;
+    transferBFinished = false;
+    DRing::downloadFile(bobId, convId, iidBob, tidBob, recvPath);
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
+        return transferAFinished && transferBFinished;
+    }));
+
+    std::remove(sendPath.c_str());
+    std::remove(recvPath.c_str());
+    DRing::unregisterSignalHandlers();
+    std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
 void
 FileTransferTest::testBadSha3sumOut()
 {
diff --git a/test/unitTest/syncHistory/syncHistory.cpp b/test/unitTest/syncHistory/syncHistory.cpp
index 2cb28faa55009b64f325cb32d4de3ecc19cad1fa..7b07f4019ddd61f85bb6b7898a6d98b9f7ac0b74 100644
--- a/test/unitTest/syncHistory/syncHistory.cpp
+++ b/test/unitTest/syncHistory/syncHistory.cpp
@@ -69,6 +69,7 @@ private:
     void testSyncCreateAccountExportDeleteReimportWithConvReq();
     void testSyncOneToOne();
     void testConversationRequestRemoved();
+    void testProfileReceivedMultiDevice();
 
     CPPUNIT_TEST_SUITE(SyncHistoryTest);
     CPPUNIT_TEST(testCreateConversationThenSync);
@@ -82,6 +83,7 @@ private:
     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportWithConvReq);
     CPPUNIT_TEST(testSyncOneToOne);
     CPPUNIT_TEST(testConversationRequestRemoved);
+    CPPUNIT_TEST(testProfileReceivedMultiDevice);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -952,6 +954,118 @@ SyncHistoryTest::testConversationRequestRemoved()
     std::remove(aliceArchive.c_str());
 }
 
+void
+SyncHistoryTest::testProfileReceivedMultiDevice()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto aliceUri = aliceAccount->getUsername();
+    auto bobUri = bobAccount->getUsername();
+
+    // Export alice
+    auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
+    std::remove(aliceArchive.c_str());
+    aliceAccount->exportArchive(aliceArchive);
+
+    // Set VCards
+    std::string vcard = "BEGIN:VCARD\n\
+VERSION:2.1\n\
+FN:TITLE\n\
+DESCRIPTION:DESC\n\
+END:VCARD";
+    auto alicePath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID()
+                     + DIR_SEPARATOR_STR + "profile.vcf";
+    auto bobPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + bobAccount->getAccountID()
+                   + DIR_SEPARATOR_STR + "profile.vcf";
+    // Save VCard
+    auto p = std::filesystem::path(alicePath);
+    fileutils::recursive_mkdir(p.parent_path());
+    std::ofstream aliceFile(alicePath);
+    if (aliceFile.is_open()) {
+        aliceFile << vcard;
+        aliceFile.close();
+    }
+    p = std::filesystem::path(bobPath);
+    fileutils::recursive_mkdir(p.parent_path());
+    std::ofstream bobFile(bobPath);
+    if (bobFile.is_open()) {
+        bobFile << vcard;
+        bobFile.close();
+    }
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
+    bool conversationReady = false, requestReceived = false, bobProfileReceived = false,
+         aliceProfileReceived = false;
+    std::string convId = "";
+    std::string bobDest = aliceAccount->dataTransfer()->profilePath(bobUri);
+    confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::IncomingTrustRequest>(
+        [&](const std::string& account_id,
+            const std::string& /*from*/,
+            const std::string& /*conversationId*/,
+            const std::vector<uint8_t>& /*payload*/,
+            time_t /*received*/) {
+            if (account_id == bobId)
+                requestReceived = true;
+            cv.notify_one();
+        }));
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>(
+        [&](const std::string& accountId, const std::string& conversationId) {
+            if (accountId == aliceId) {
+                convId = conversationId;
+            } else if (accountId == bobId) {
+                conversationReady = true;
+            }
+            cv.notify_one();
+        }));
+    confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::ProfileReceived>(
+        [&](const std::string& accountId, const std::string& peerId, const std::string& path) {
+            if (accountId == aliceId && peerId == bobUri) {
+                bobProfileReceived = true;
+                auto p = std::filesystem::path(bobDest);
+                fileutils::recursive_mkdir(p.parent_path());
+                std::rename(path.c_str(), bobDest.c_str());
+            } else if (accountId == bobId && peerId == aliceUri) {
+                aliceProfileReceived = true;
+            } else if (accountId == alice2Id && peerId == bobUri) {
+                bobProfileReceived = true;
+            } else if (accountId == alice2Id && peerId == aliceUri) {
+                aliceProfileReceived = true;
+            }
+            cv.notify_one();
+        }));
+    DRing::registerSignalHandlers(confHandlers);
+    aliceAccount->addContact(bobUri);
+    aliceAccount->sendTrustRequest(bobUri, {});
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+
+    CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
+        return conversationReady && bobProfileReceived && aliceProfileReceived;
+    }));
+    CPPUNIT_ASSERT(fileutils::isFile(bobDest));
+
+    // Now create alice2
+    std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
+    details[ConfProperties::TYPE] = "RING";
+    details[ConfProperties::DISPLAYNAME] = "ALICE2";
+    details[ConfProperties::ALIAS] = "ALICE2";
+    details[ConfProperties::UPNP_ENABLED] = "true";
+    details[ConfProperties::ARCHIVE_PASSWORD] = "";
+    details[ConfProperties::ARCHIVE_PIN] = "";
+    details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
+    bobProfileReceived = false, aliceProfileReceived = false;
+    alice2Id = Manager::instance().addAccount(details);
+    std::remove(aliceArchive.c_str());
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&] {
+        return aliceProfileReceived && bobProfileReceived;
+    }));
+    DRing::unregisterSignalHandlers();
+}
+
 } // namespace test
 } // namespace jami