diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 145ab265828cb4f2f395625bfee0f296983f5965..0071e04706927683afe23b28bb5771117adedd8c 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -1946,6 +1946,23 @@
            </arg>
        </signal>
 
+       <signal name="conversationRequestDeclined" tp:name-for-bindings="conversationRequestDeclined">
+           <tp:added version="10.1.0"/>
+           <tp:docstring>
+               Notify clients when a new conversation request is declined
+           </tp:docstring>
+           <arg type="s" name="account_id">
+               <tp:docstring>
+                   Account id related
+               </tp:docstring>
+           </arg>
+           <arg type="s" name="conversation_id">
+               <tp:docstring>
+                   Conversation id
+               </tp:docstring>
+           </arg>
+       </signal>
+
        <signal name="conversationReady" tp:name-for-bindings="conversationReady">
            <tp:added version="10.0.0"/>
            <tp:docstring>
diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp
index 3a4b6af132ac838eee84b25454888ad2b4454e00..248585343f942b3380d6a8bb6b8d15769d662226 100644
--- a/bin/dbus/dbusclient.cpp
+++ b/bin/dbus/dbusclient.cpp
@@ -303,6 +303,8 @@ DBusClient::initLibrary(int flags)
             bind(&DBusConfigurationManager::messageReceived, confM, _1, _2, _3)),
         exportable_callback<ConversationSignal::ConversationRequestReceived>(
             bind(&DBusConfigurationManager::conversationRequestReceived, confM, _1, _2, _3)),
+        exportable_callback<ConversationSignal::ConversationRequestDeclined>(
+            bind(&DBusConfigurationManager::conversationRequestDeclined, confM, _1, _2)),
         exportable_callback<ConversationSignal::ConversationReady>(
             bind(&DBusConfigurationManager::conversationReady, confM, _1, _2)),
         exportable_callback<ConversationSignal::ConversationRemoved>(
diff --git a/bin/jni/conversation.i b/bin/jni/conversation.i
index 7858045b8481522a7f354bcea27b2cfc66e11544..ba31e46e9fb74fa1690197c21e44164643bab289 100644
--- a/bin/jni/conversation.i
+++ b/bin/jni/conversation.i
@@ -28,6 +28,7 @@ public:
     virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){}
     virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){}
     virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){}
+    virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){}
@@ -66,6 +67,7 @@ public:
     virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){}
     virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){}
     virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){}
+    virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){}
diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i
index 5e83fcac81f4dd1581f77e772699780911e838d6..010ad84cc66bfd4aeb78d56898fb391fcd95cdac 100644
--- a/bin/jni/jni_interface.i
+++ b/bin/jni/jni_interface.i
@@ -320,6 +320,7 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM
         exportable_callback<ConversationSignal::ConversationLoaded>(bind(&ConversationCallback::conversationLoaded, convM, _1, _2, _3, _4)),
         exportable_callback<ConversationSignal::MessageReceived>(bind(&ConversationCallback::messageReceived, convM, _1, _2, _3)),
         exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&ConversationCallback::conversationRequestReceived, convM, _1, _2, _3)),
+        exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&ConversationCallback::conversationRequestDeclined, convM, _1, _2)),
         exportable_callback<ConversationSignal::ConversationReady>(bind(&ConversationCallback::conversationReady, convM, _1, _2)),
         exportable_callback<ConversationSignal::ConversationRemoved>(bind(&ConversationCallback::conversationRemoved, convM, _1, _2)),
         exportable_callback<ConversationSignal::ConversationMemberEvent>(bind(&ConversationCallback::conversationMemberEvent, convM, _1, _2, _3, _4)),
diff --git a/bin/nodejs/callback.h b/bin/nodejs/callback.h
index c1da6535c28bb51ef5b60f40ae47dc849faa33e3..480a7c88f9d24c6dfc12a1d7510d206de1cc0f25 100644
--- a/bin/nodejs/callback.h
+++ b/bin/nodejs/callback.h
@@ -30,6 +30,7 @@ Persistent<Function> incomingCallWithMediaCb;
 Persistent<Function> conversationLoadedCb;
 Persistent<Function> messageReceivedCb;
 Persistent<Function> conversationRequestReceivedCb;
+Persistent<Function> conversationRequestDeclinedCb;
 Persistent<Function> conversationReadyCb;
 Persistent<Function> conversationRemovedCb;
 Persistent<Function> conversationMemberEventCb;
@@ -93,6 +94,8 @@ getPresistentCb(std::string_view signal)
         return &conversationRemovedCb;
     else if (signal == "ConversationRequestReceived")
         return &conversationRequestReceivedCb;
+    else if (signal == "ConversationRequestDeclined")
+        return &conversationRequestDeclinedCb;
     else if (signal == "ConversationMemberEvent")
         return &conversationMemberEventCb;
     else if (signal == "OnConversationError")
@@ -569,6 +572,23 @@ conversationRequestReceived(const std::string& accountId, const std::string& con
     uv_async_send(&signalAsync);
 }
 
+void
+conversationRequestDeclined(const std::string& accountId, const std::string& conversationId)
+{
+    std::lock_guard<std::mutex> lock(pendingSignalsLock);
+    pendingSignals.emplace([accountId, conversationId, message]() {
+        Local<Function> func = Local<Function>::New(Isolate::GetCurrent(), conversationRequestDeclinedCb);
+        if (!func.IsEmpty()) {
+            SWIGV8_VALUE callback_args[] = {
+                V8_STRING_NEW_LOCAL(accountId),
+                V8_STRING_NEW_LOCAL(conversationId)
+            };
+            func->Call(SWIGV8_CURRENT_CONTEXT(), SWIGV8_NULL(), 2, callback_args);
+        }
+    });
+    uv_async_send(&signalAsync);
+}
+
 void
 conversationReady(const std::string& accountId, const std::string& conversationId)
 {
diff --git a/bin/nodejs/conversation.i b/bin/nodejs/conversation.i
index 45605ae27b36cd09ada9a5e0dd95afd7d5cbdc9d..5a143ad151b8d002e2179f721f49f6bf5328debc 100644
--- a/bin/nodejs/conversation.i
+++ b/bin/nodejs/conversation.i
@@ -28,6 +28,7 @@ public:
     virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){}
     virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){}
     virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){}
+    virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){}
@@ -67,6 +68,7 @@ public:
     virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){}
     virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){}
     virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){}
+    virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){}
     virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){}
diff --git a/bin/nodejs/nodejs_interface.i b/bin/nodejs/nodejs_interface.i
index d64d875ee7e0f50b19813fcabc3827fd0318c383..1dd9511dc8879af584c15fd54f9ef390b6358fde 100644
--- a/bin/nodejs/nodejs_interface.i
+++ b/bin/nodejs/nodejs_interface.i
@@ -148,6 +148,7 @@ void init(const SWIGV8_VALUE& funcMap){
         exportable_callback<ConversationSignal::ConversationLoaded>(bind(&conversationLoaded, _1, _2, _3, _4)),
         exportable_callback<ConversationSignal::MessageReceived>(bind(&messageReceived, _1, _2, _3)),
         exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&conversationRequestReceived, _1, _2, _3)),
+        exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&conversationRequestDeclined, _1, _2)),
         exportable_callback<ConversationSignal::ConversationReady>(bind(&conversationReady, _1, _2)),
         exportable_callback<ConversationSignal::ConversationRemoved>(bind(&conversationRemoved, _1, _2)),
         exportable_callback<ConversationSignal::ConversationMemberEvent>(bind(&conversationMemberEvent, _1, _2, _3, _4)),
diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp
index a306c0765480833bec251157c4cda93543883f19..dcccdedbd705441469576e534b60d07aacc5e7f9 100644
--- a/src/client/ring_signal.cpp
+++ b/src/client/ring_signal.cpp
@@ -131,6 +131,7 @@ getSignalHandlers()
         exported_callback<DRing::ConversationSignal::ConversationLoaded>(),
         exported_callback<DRing::ConversationSignal::MessageReceived>(),
         exported_callback<DRing::ConversationSignal::ConversationRequestReceived>(),
+        exported_callback<DRing::ConversationSignal::ConversationRequestDeclined>(),
         exported_callback<DRing::ConversationSignal::ConversationReady>(),
         exported_callback<DRing::ConversationSignal::ConversationRemoved>(),
         exported_callback<DRing::ConversationSignal::ConversationMemberEvent>(),
diff --git a/src/jami/conversation_interface.h b/src/jami/conversation_interface.h
index ada5792746bd5ea59dcb4ac1e88eda94d1f9155c..69890a9a54dafe9882c85ab492b4a27884d32d77 100644
--- a/src/jami/conversation_interface.h
+++ b/src/jami/conversation_interface.h
@@ -98,6 +98,12 @@ struct DRING_PUBLIC ConversationSignal
                              const std::string& /* conversationId */,
                              std::map<std::string, std::string> /*metadatas*/);
     };
+    struct DRING_PUBLIC ConversationRequestDeclined
+    {
+        constexpr static const char* name = "ConversationRequestDeclined";
+        using cb_type = void(const std::string& /*accountId*/,
+                             const std::string& /* conversationId */);
+    };
     struct DRING_PUBLIC ConversationReady
     {
         constexpr static const char* name = "ConversationReady";
diff --git a/src/jamidht/account_manager.cpp b/src/jamidht/account_manager.cpp
index 0272f6c558c28b440f3f1dd996a1526915c8c596..ec8a9494c0f194ce92c3e988fe1bebcf7eec54d9 100644
--- a/src/jamidht/account_manager.cpp
+++ b/src/jamidht/account_manager.cpp
@@ -532,14 +532,22 @@ AccountManager::getRequest(const std::string& id) const
     return std::nullopt;
 }
 
-void
+bool
 AccountManager::addConversationRequest(const std::string& id, const ConversationRequest& req)
 {
     if (info_) {
         std::lock_guard<std::mutex> lk(conversationsRequestsMtx);
+        auto it = info_->conversationsRequests.find(id);
+        if (it != info_->conversationsRequests.end()) {
+            // Check if updated
+            if (req == it->second)
+                return false;
+        }
         info_->conversationsRequests[id] = req;
         saveConvRequests();
+        return true;
     }
+    return false;
 }
 
 void
@@ -724,7 +732,8 @@ AccountManager::forEachDevice(
 
     struct State
     {
-        unsigned remaining {1}; // Note: state is initialized to 1, because we need to wait that the get is finished
+        unsigned remaining {
+            1}; // Note: state is initialized to 1, because we need to wait that the get is finished
         std::set<dht::PkId> treatedDevices {};
         std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)> onDevice;
         std::function<void(bool)> onEnd;
@@ -767,9 +776,7 @@ AccountManager::forEachDevice(
             });
             return true;
         },
-        [state](bool /*ok*/) {
-            state->found({});
-        });
+        [state](bool /*ok*/) { state->found({}); });
 }
 
 void
diff --git a/src/jamidht/account_manager.h b/src/jamidht/account_manager.h
index 0fb6177b6938e0177dde2af1e174283f2201c460..1fe1fc379f606f6167dd849fa7a0e9affebc607e 100644
--- a/src/jamidht/account_manager.h
+++ b/src/jamidht/account_manager.h
@@ -231,7 +231,7 @@ public:
     void addConversation(const ConvInfo& info);
     void setConversationsRequests(const std::map<std::string, ConversationRequest>& newConvReq);
     std::optional<ConversationRequest> getRequest(const std::string& id) const;
-    void addConversationRequest(const std::string& id, const ConversationRequest& req);
+    bool addConversationRequest(const std::string& id, const ConversationRequest& req);
     void rmConversationRequest(const std::string& id);
     mutable std::mutex conversationsRequestsMtx;
 
diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp
index b802bd87d3fb86e550f6eb00d588f9426bdfcee7..3e873b002a2710fc5fbdfd026d0dac041239c167 100644
--- a/src/jamidht/conversation.cpp
+++ b/src/jamidht/conversation.cpp
@@ -103,6 +103,8 @@ ConversationRequest::toMap() const
     auto result = metadatas;
     result["id"] = conversationId;
     result["from"] = from;
+    if (declined)
+        result["declined"] = std::to_string(declined);
     result["received"] = std::to_string(received);
     return result;
 }
diff --git a/src/jamidht/conversation.h b/src/jamidht/conversation.h
index fa0fa2047ea2c49b232338a9b3de1eed211f5fd9..80555c666a1096f3e00a659d3abb3134bf61dbb0 100644
--- a/src/jamidht/conversation.h
+++ b/src/jamidht/conversation.h
@@ -52,6 +52,13 @@ struct ConversationRequest
     Json::Value toJson() const;
     std::map<std::string, std::string> toMap() const;
 
+    bool operator==(const ConversationRequest& o) const
+    {
+        auto m = toMap();
+        auto om = o.toMap();
+        return m.size() == om.size() && std::equal(m.begin(), m.end(), om.begin());
+    }
+
     MSGPACK_DEFINE_MAP(from, conversationId, metadatas, received, declined)
 };
 
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 566427027cef6f73d01edcb8e6d005586f71fee6..5910a1c50d25005c18c365ec4f8734e0003212ee 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -3888,6 +3888,8 @@ JamiAccount::declineConversationRequest(const std::string& conversationId)
         return;
     request->declined = std::time(nullptr);
     accountManager_->addConversationRequest(conversationId, std::move(*request));
+    emitSignal<DRing::ConversationSignal::ConversationRequestDeclined>(getAccountID(),
+                                                                       conversationId);
 
     syncWithConnected();
 }
@@ -5037,10 +5039,19 @@ JamiAccount::cacheSyncConnection(std::shared_ptr<ChannelSocket>&& socket,
             }
 
             // New request
-            accountManager_->addConversationRequest(convId, req);
-
-            if (req.declined != 0)
-                continue; // Request removed, do not emit signal
+            if (!accountManager_->addConversationRequest(convId, req))
+                continue; // Already added
+
+            if (req.declined != 0) {
+                // Request declined
+                JAMI_INFO("[Account %s] Declined request detected for conversation %s (device %s)",
+                          getAccountID().c_str(),
+                          convId.c_str(),
+                          deviceId.to_c_str());
+                emitSignal<DRing::ConversationSignal::ConversationRequestDeclined>(getAccountID(),
+                                                                                   convId);
+                continue;
+            }
 
             JAMI_INFO("[Account %s] New request detected for conversation %s (device %s)",
                       getAccountID().c_str(),
diff --git a/test/unitTest/syncHistory/syncHistory.cpp b/test/unitTest/syncHistory/syncHistory.cpp
index bc4dae93eeec018774d74a4c49fb93cfb4fb14d0..be8e461671af5685b428d8abbbc21c1f3c0c33e0 100644
--- a/test/unitTest/syncHistory/syncHistory.cpp
+++ b/test/unitTest/syncHistory/syncHistory.cpp
@@ -68,6 +68,7 @@ private:
     void testSyncCreateAccountExportDeleteReimportWithConvId();
     void testSyncCreateAccountExportDeleteReimportWithConvReq();
     void testSyncOneToOne();
+    void testConversationRequestRemoved();
 
     CPPUNIT_TEST_SUITE(SyncHistoryTest);
     CPPUNIT_TEST(testCreateConversationThenSync);
@@ -80,6 +81,7 @@ private:
     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportWithConvId);
     CPPUNIT_TEST(testSyncCreateAccountExportDeleteReimportWithConvReq);
     CPPUNIT_TEST(testSyncOneToOne);
+    CPPUNIT_TEST(testConversationRequestRemoved);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -862,6 +864,93 @@ SyncHistoryTest::testSyncOneToOne()
     std::remove(aliceArchive.c_str());
 }
 
+void
+SyncHistoryTest::testConversationRequestRemoved()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+
+    // Export alice
+    auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
+    std::remove(aliceArchive.c_str());
+    aliceAccount->exportArchive(aliceArchive);
+
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto uri = aliceAccount->getUsername();
+
+    // Start conversation for Alice
+    auto convId = bobAccount->startConversation();
+
+    // Check that alice receives the request
+    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;
+    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 == aliceId && conversationId == convId) {
+                    requestReceived = true;
+                    cv.notify_one();
+                }
+            }));
+    DRing::registerSignalHandlers(confHandlers);
+
+    bobAccount->addConversationMember(convId, uri);
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    DRing::unregisterSignalHandlers();
+    confHandlers.clear();
+
+    // 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;
+    alice2Id = Manager::instance().addAccount(details);
+
+    requestReceived = false;
+    bool requestDeclined = false, requestDeclined2 = 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 == alice2Id && conversationId == convId) {
+                    requestReceived = true;
+                    cv.notify_one();
+                }
+            }));
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestDeclined>(
+            [&](const std::string& accountId, const std::string& conversationId) {
+                if (conversationId != convId)
+                    return;
+                if (accountId == aliceId)
+                    requestDeclined = true;
+                if (accountId == alice2Id)
+                    requestDeclined2 = true;
+                cv.notify_one();
+            }));
+    DRing::registerSignalHandlers(confHandlers);
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    // Now decline trust request, this should trigger ConversationRequestDeclined both sides for Alice
+    aliceAccount->declineConversationRequest(convId);
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] {
+        return requestDeclined && requestDeclined2;
+    }));
+
+    DRing::unregisterSignalHandlers();
+    std::remove(aliceArchive.c_str());
+}
+
 } // namespace test
 } // namespace jami