diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp
index edd4adc03bcf67c39aeaeb1c8661f46c2d99314f..983fe1aec536bb0eeee138cea1614c2b70d62d81 100644
--- a/src/jamidht/conversationrepository.cpp
+++ b/src/jamidht/conversationrepository.cpp
@@ -948,11 +948,13 @@ ConversationRepository::Impl::checkVote(const std::string& userDevice,
             JAMI_ERR("Detected vote for self: %s", votedUri.c_str());
             return false;
         }
-        // file in members or admin or invited
-        auto invitedFile = fmt::format("invited/{}", votedUri);
-        if (!memberCertificate(votedUri, treeOld) && !fileAtTree(invitedFile, treeOld)) {
-            JAMI_ERR("No member file found for vote: %s", votedUri.c_str());
-            return false;
+        if (voteType == "ban") {
+            // file in members or admin or invited
+            auto invitedFile = fmt::format("invited/{}", votedUri);
+            if (!memberCertificate(votedUri, treeOld) && !fileAtTree(invitedFile, treeOld)) {
+                JAMI_ERR("No member file found for vote: %s", votedUri.c_str());
+                return false;
+            }
         }
     } else {
         // Check not current device
@@ -1067,14 +1069,17 @@ ConversationRepository::Impl::checkValidJoins(const std::string& userDevice,
         return false;
     // Check no other files changed
     auto changedFiles = ConversationRepository::changedFiles(diffStats(commitId, parentId));
-    std::size_t wantedChanged = 3;
-    if (changedFiles.size() != wantedChanged)
-        return false;
-
     auto invitedFile = fmt::format("invited/{}", uriMember);
     auto membersFile = fmt::format("members/{}.crt", uriMember);
     auto deviceFile = fmt::format("devices/{}.crt", userDevice);
 
+    for (auto& file : changedFiles) {
+        if (file != invitedFile && file != membersFile && file != deviceFile) {
+            JAMI_ERR("Unwanted file %s found", file.c_str());
+            return false;
+        }
+    }
+
     // Retrieve tree for commits
     auto repo = repository();
     assert(repo);
@@ -1108,10 +1113,6 @@ ConversationRepository::Impl::checkValidJoins(const std::string& userDevice,
         JAMI_ERR("%s devices not found", userUri.c_str());
         return false;
     }
-    if (fileAtTree(deviceFile, treeOld)) {
-        JAMI_ERR("%s devices found too soon", userUri.c_str());
-        return false;
-    }
 
     return true;
 }
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 5e8ae81136c64221cf89b94dc5f45daf80e006fe..f00fc995de6713d70d6f8dd7da80a2416c451d5e 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -4377,7 +4377,10 @@ JamiAccount::askForProfile(const std::string& conversationId,
     if (!connectionManager_)
         return;
 
-    auto channelName = DATA_TRANSFER_URI + conversationId + "/profile/" + memberUri + ".vcf";
+    auto channelName = fmt::format("{}{}/profile/{}.vcf",
+                                   DATA_TRANSFER_URI,
+                                   conversationId,
+                                   memberUri);
     // We can avoid to negotiate new sessions, as the file notif
     // probably come from an online device or last connected device.
     connectionManager_->connectDevice(
diff --git a/test/unitTest/conversation/conversationMembersEvent.cpp b/test/unitTest/conversation/conversationMembersEvent.cpp
index 12e69db0ea7830860738f2f1829db18323619c9e..9c4ad3845a8c6c602023abd05b0e107ba107ed1d 100644
--- a/test/unitTest/conversation/conversationMembersEvent.cpp
+++ b/test/unitTest/conversation/conversationMembersEvent.cpp
@@ -84,6 +84,7 @@ public:
     void testAvoidTwoOneToOne();
     void testAvoidTwoOneToOneMultiDevices();
     void testRemoveRequestBannedMultiDevices();
+    void testBanUnbanMultiDevice();
 
     std::string aliceId;
     std::string bobId;
@@ -121,6 +122,7 @@ private:
     CPPUNIT_TEST(testAvoidTwoOneToOne);
     CPPUNIT_TEST(testAvoidTwoOneToOneMultiDevices);
     CPPUNIT_TEST(testRemoveRequestBannedMultiDevices);
+    CPPUNIT_TEST(testBanUnbanMultiDevice);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -2270,6 +2272,123 @@ ConversationMembersEventTest::testRemoveRequestBannedMultiDevices()
     CPPUNIT_ASSERT(DRing::getConversationRequests(bob2Id).size() == 0);
 }
 
+void
+ConversationMembersEventTest::testBanUnbanMultiDevice()
+{
+    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;
+    auto requestReceived = false, requestReceivedBob2 = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>(
+            [&](const std::string& accountId,
+                const std::string& conversationId,
+                std::map<std::string, std::string> /*metadatas*/) {
+                if (accountId == bobId)
+                    requestReceived = true;
+                else if (accountId == bob2Id)
+                    requestReceivedBob2 = true;
+                cv.notify_one();
+            }));
+    auto bob2Started = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::VolatileDetailsChanged>(
+            [&](const std::string& accountId, const std::map<std::string, std::string>& details) {
+                if (accountId == bob2Id) {
+                    auto daemonStatus = details.at(
+                        DRing::Account::VolatileProperties::DEVICE_ANNOUNCED);
+                    if (daemonStatus == "true")
+                        bob2Started = true;
+                }
+                cv.notify_one();
+            }));
+    auto bob2ContactRemoved = false;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::ContactRemoved>(
+        [&](const std::string& accountId, const std::string& uri, bool banned) {
+            if (accountId == bob2Id && uri == aliceUri && banned) {
+                bob2ContactRemoved = true;
+            }
+            cv.notify_one();
+        }));
+    auto memberMessageGenerated = false, voteMessageGenerated = false;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>(
+        [&](const std::string& accountId,
+            const std::string& conversationId,
+            std::map<std::string, std::string> message) {
+            auto itFind = message.find("type");
+            if (itFind == message.end())
+                return;
+            if (accountId == aliceId && conversationId == convId) {
+                if (itFind->second == "member")
+                    memberMessageGenerated = true;
+                if (itFind->second == "vote")
+                    voteMessageGenerated = true;
+            }
+            cv.notify_one();
+        }));
+    auto conversationReadyBob = false, conversationReadyBob2 = false;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>(
+        [&](const std::string& accountId, const std::string& conversationId) {
+            if (accountId == bobId && conversationId == convId) {
+                conversationReadyBob = true;
+            } else if (accountId == bob2Id && conversationId == convId) {
+                conversationReadyBob2 = true;
+            }
+            cv.notify_one();
+        }));
+    DRing::registerSignalHandlers(confHandlers);
+
+    // Bob creates a second device
+    auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
+    std::remove(bobArchive.c_str());
+    bobAccount->exportArchive(bobArchive);
+    std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
+    details[ConfProperties::TYPE] = "RING";
+    details[ConfProperties::DISPLAYNAME] = "BOB2";
+    details[ConfProperties::ALIAS] = "BOB2";
+    details[ConfProperties::UPNP_ENABLED] = "true";
+    details[ConfProperties::ARCHIVE_PASSWORD] = "";
+    details[ConfProperties::ARCHIVE_PIN] = "";
+    details[ConfProperties::ARCHIVE_PATH] = bobArchive;
+    bob2Id = Manager::instance().addAccount(details);
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; }));
+
+    // Alice adds bob
+    requestReceived = false;
+    DRing::addConversationMember(aliceId, convId, bobUri);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
+        return memberMessageGenerated && requestReceived && requestReceivedBob2;
+    }));
+
+    // Alice kick Bob while invited
+    memberMessageGenerated = false;
+    voteMessageGenerated = false;
+    DRing::removeConversationMember(aliceId, convId, bobUri);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; }));
+
+    // Alice re-add Bob while invited
+    memberMessageGenerated = false;
+    voteMessageGenerated = false;
+    DRing::addConversationMember(aliceId, convId, bobUri);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; }));
+
+    // bob accepts
+    DRing::acceptConversationRequest(bobId, convId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && conversationReadyBob2; }));
+}
+
 } // namespace test
 } // namespace jami