diff --git a/src/account.cpp b/src/account.cpp
index 6e9a1f3c510a7a7f26b04613b4497f46aa840af5..a6bfb6826b0ee87ea09b9e86fd116b7585db1e21 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -82,6 +82,7 @@ const char* const Account::PASSWORD_KEY = "password";
 const char* const Account::HOSTNAME_KEY = "hostname";
 const char* const Account::ACCOUNT_ENABLE_KEY = "enable";
 const char* const Account::ACCOUNT_AUTOANSWER_KEY = "autoAnswer";
+const char* const Account::ACCOUNT_READRECEIPT_KEY = "sendReadReceipt";
 const char* const Account::ACCOUNT_ISRENDEZVOUS_KEY = "rendezVous";
 const char* const Account::ACCOUNT_ACTIVE_CALL_LIMIT_KEY = "activeCallLimit";
 const char* const Account::MAILBOX_KEY = "mailbox";
@@ -96,9 +97,10 @@ const char* const Account::LOCAL_MODERATORS_ENABLED_KEY = "localModeratorsEnable
 const char* const Account::ALL_MODERATORS_ENABLED_KEY = "allModeratorsEnabled";
 
 #ifdef __ANDROID__
-constexpr const char* const DEFAULT_RINGTONE_PATH = "/data/data/cx.ring/files/ringtones/default.opus";
+constexpr const char* const DEFAULT_RINGTONE_PATH
+    = "/data/data/cx.ring/files/ringtones/default.opus";
 #else
-constexpr const char* const DEFAULT_RINGTONE_PATH =  PROGSHAREDIR "/ringtones/default.opus";
+constexpr const char* const DEFAULT_RINGTONE_PATH = PROGSHAREDIR "/ringtones/default.opus";
 #endif
 
 Account::Account(const std::string& accountID)
@@ -109,6 +111,7 @@ Account::Account(const std::string& accountID)
     , alias_()
     , enabled_(true)
     , autoAnswerEnabled_(false)
+    , sendReadReceipt_(true)
     , isRendezVous_(false)
     , registrationState_(RegistrationState::UNREGISTERED)
     , systemCodecContainer_(getSystemCodecContainer())
@@ -122,7 +125,8 @@ Account::Account(const std::string& accountID)
     , upnpEnabled_(true)
     , localModeratorsEnabled_(true)
     , allModeratorsEnabled_(true)
-#if (defined(__linux__) and not defined(__ANDROID__)) || defined(WIN32) || (defined(__APPLE__) && TARGET_OS_MAC)
+#if (defined(__linux__) and not defined(__ANDROID__)) || defined(WIN32) \
+    || (defined(__APPLE__) && TARGET_OS_MAC)
     , multiStreamEnabled_(true)
 #else
     , multiStreamEnabled_(false)
@@ -234,6 +238,7 @@ Account::serialize(YAML::Emitter& out) const
     out << YAML::Key << ACTIVE_CODEC_KEY << YAML::Value << activeCodecs;
     out << YAML::Key << MAILBOX_KEY << YAML::Value << mailBox_;
     out << YAML::Key << ACCOUNT_AUTOANSWER_KEY << YAML::Value << autoAnswerEnabled_;
+    out << YAML::Key << ACCOUNT_READRECEIPT_KEY << YAML::Value << sendReadReceipt_;
     out << YAML::Key << ACCOUNT_ISRENDEZVOUS_KEY << YAML::Value << isRendezVous_;
     out << YAML::Key << ACCOUNT_ACTIVE_CALL_LIMIT_KEY << YAML::Value << activeCallLimit_;
     out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_;
@@ -257,6 +262,7 @@ Account::unserialize(const YAML::Node& node)
     parseValue(node, ALIAS_KEY, alias_);
     parseValue(node, ACCOUNT_ENABLE_KEY, enabled_);
     parseValue(node, ACCOUNT_AUTOANSWER_KEY, autoAnswerEnabled_);
+    parseValueOptional(node, ACCOUNT_READRECEIPT_KEY, sendReadReceipt_);
     parseValueOptional(node, ACCOUNT_ISRENDEZVOUS_KEY, isRendezVous_);
     parseValue(node, ACCOUNT_ACTIVE_CALL_LIMIT_KEY, activeCallLimit_);
     // parseValue(node, PASSWORD_KEY, password_);
@@ -314,6 +320,7 @@ Account::setAccountDetails(const std::map<std::string, std::string>& details)
     parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_);
     parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_);
     parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_);
+    parseBool(details, Conf::CONFIG_ACCOUNT_SENDREADRECEIPT, sendReadReceipt_);
     parseBool(details, Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_);
     parseInt(details, DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, activeCallLimit_);
     parseBool(details, Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_);
@@ -348,6 +355,7 @@ Account::getAccountDetails() const
             {Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_},
             {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_ ? TRUE_STR : FALSE_STR},
             {Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_ ? TRUE_STR : FALSE_STR},
+            {Conf::CONFIG_ACCOUNT_SENDREADRECEIPT, sendReadReceipt_ ? TRUE_STR : FALSE_STR},
             {Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_ ? TRUE_STR : FALSE_STR},
             {DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, std::to_string(activeCallLimit_)},
             {Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_ ? TRUE_STR : FALSE_STR},
@@ -712,9 +720,8 @@ bool
 Account::meetMinimumRequiredVersion(const std::vector<unsigned>& version,
                                     const std::vector<unsigned>& minRequiredVersion)
 {
-    for (size_t i=0; i<minRequiredVersion.size(); i++) {
-        if (i == version.size() or
-            version[i] < minRequiredVersion[i])
+    for (size_t i = 0; i < minRequiredVersion.size(); i++) {
+        if (i == version.size() or version[i] < minRequiredVersion[i])
             return false;
         if (version[i] > minRequiredVersion[i])
             return true;
diff --git a/src/account.h b/src/account.h
index 637dc275d913f80d4b0781cbc53cf9851d1685e5..51cc8bcb053d0305602053ad998332792d7b2c74 100644
--- a/src/account.h
+++ b/src/account.h
@@ -285,6 +285,8 @@ public:
 
     bool isAutoAnswerEnabled() const { return autoAnswerEnabled_; }
 
+    bool isReadReceiptEnabled() const { return sendReadReceipt_; }
+
     void attachCall(const std::string& id);
     void detachCall(const std::string& id);
 
@@ -458,6 +460,7 @@ protected:
     static const char* const HOSTNAME_KEY;
     static const char* const ACCOUNT_ENABLE_KEY;
     static const char* const ACCOUNT_AUTOANSWER_KEY;
+    static const char* const ACCOUNT_READRECEIPT_KEY;
     static const char* const ACCOUNT_ISRENDEZVOUS_KEY;
     static const char* const ACCOUNT_ACTIVE_CALL_LIMIT_KEY;
     static const char* const MAILBOX_KEY;
@@ -521,6 +524,9 @@ protected:
     /* If true, automatically answer calls to this account */
     bool autoAnswerEnabled_;
 
+    // If true, send Displayed status (and emit to the client)
+    bool sendReadReceipt_;
+
     /* If true mix calls into a conference */
     bool isRendezVous_;
 
diff --git a/src/account_schema.h b/src/account_schema.h
index 8485ecb7308c9b8f2073d52075dde2c62b1fd42d..2e20e9d3a570de1675d1df3e9fa68cb2cb3783cb 100644
--- a/src/account_schema.h
+++ b/src/account_schema.h
@@ -37,6 +37,7 @@ static const char* const CONFIG_ACCOUNT_DISPLAYNAME = "Account.displayName";
 static const char* const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox";
 static const char* const CONFIG_ACCOUNT_ENABLE = "Account.enable";
 static const char* const CONFIG_ACCOUNT_AUTOANSWER = "Account.autoAnswer";
+static const char* const CONFIG_ACCOUNT_SENDREADRECEIPT = "Account.sendReadReceipt";
 static const char* const CONFIG_ACCOUNT_ISRENDEZVOUS = "Account.rendezVous";
 static const char* const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire";
 static const char* const CONFIG_ACCOUNT_DTMF_TYPE = "Account.dtmfType";
diff --git a/src/jami/account_const.h b/src/jami/account_const.h
index cdf7568e4cb8401f4365622c5f3bb24ac223d7b5..c3ef7425e7397cd0d2522433dd10557eb21ed4a6 100644
--- a/src/jami/account_const.h
+++ b/src/jami/account_const.h
@@ -111,6 +111,7 @@ constexpr static const char ENABLED[] = "Account.enable";
 constexpr static const char MAILBOX[] = "Account.mailbox";
 constexpr static const char DTMF_TYPE[] = "Account.dtmfType";
 constexpr static const char AUTOANSWER[] = "Account.autoAnswer";
+constexpr static const char SENDREADRECEIPT[] = "Account.sendReadReceipt";
 constexpr static const char ISRENDEZVOUS[] = "Account.rendezVous";
 constexpr static const char ACTIVE_CALL_LIMIT[] = "Account.activeCallLimit";
 constexpr static const char HOSTNAME[] = "Account.hostname";
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 91d6465e79623ce2368b823641fa0753adc43d4d..73bd60203be71e8a83da62627ead1a3eb67dc1b0 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -611,6 +611,8 @@ SIPAccountBase::onTextMessage(const std::string& id,
                     conversationId = matched_pattern[1];
                 }
 
+                if (!isReadReceiptEnabled())
+                    return;
                 if (conversationId.empty()) // Old method
                     messageEngine_.onMessageDisplayed(from, from_hex_string(messageId), isDisplayed);
                 else if (isDisplayed) {
@@ -710,7 +712,7 @@ SIPAccountBase::setMessageDisplayed(const std::string& conversationUri,
         conversationId = uri.authority();
     if (!conversationId.empty())
         onMessageDisplayed(getUsername(), conversationId, messageId);
-    if (status == (int) DRing::Account::MessageStates::DISPLAYED)
+    if (status == (int) DRing::Account::MessageStates::DISPLAYED && isReadReceiptEnabled())
         sendInstantMessage(uri.authority(),
                            {{MIME_TYPE_IMDN, getDisplayed(conversationId, messageId)}});
     return true;
diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp
index d99685b2dc8f30cc519389eeb926c53d50126df9..2afb3cd6d03854a5a25484d769a19ac46745d72f 100644
--- a/test/unitTest/conversation/conversation.cpp
+++ b/test/unitTest/conversation/conversation.cpp
@@ -110,6 +110,7 @@ private:
     void testPingPongMessages();
     void testIsComposing();
     void testSetMessageDisplayed();
+    void testSetMessageDisplayedPreference();
     void testRemoveMember();
     void testMemberBanNoBadFile();
     void testMemberTryToRemoveAdmin();
@@ -120,6 +121,7 @@ private:
     void testVoteNonEmpty();
     void testAdminCannotKickTheirself();
     void testCommitUnauthorizedUser();
+
     // LATER void testBanDevice();
     // LATER void testBannedDeviceCannotSendMessageButMemberCan();
     // LATER void testRevokedDeviceCannotSendMessage();
@@ -177,6 +179,7 @@ private:
     CPPUNIT_TEST(testPingPongMessages);
     CPPUNIT_TEST(testIsComposing);
     CPPUNIT_TEST(testSetMessageDisplayed);
+    CPPUNIT_TEST(testSetMessageDisplayedPreference);
     CPPUNIT_TEST(testRemoveMember);
     CPPUNIT_TEST(testMemberBanNoBadFile);
     CPPUNIT_TEST(testMemberTryToRemoveAdmin);
@@ -1294,6 +1297,96 @@ ConversationTest::testSetMessageDisplayed()
     DRing::unregisterSignalHandlers();
 }
 
+void
+ConversationTest::testSetMessageDisplayedPreference()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto aliceUri = aliceAccount->getUsername();
+    auto bobUri = bobAccount->getUsername();
+    auto convId = aliceAccount->startConversation();
+    auto details = aliceAccount->getAccountDetails();
+    CPPUNIT_ASSERT(details[ConfProperties::SENDREADRECEIPT] == "true");
+    details[ConfProperties::SENDREADRECEIPT] = "false";
+    DRing::setAccountDetails(aliceId, details);
+    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, memberMessageGenerated = false,
+         msgDisplayed = false;
+    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 && conversationId == convId) {
+                conversationReady = true;
+                cv.notify_one();
+            }
+        }));
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>(
+        [&](const std::string& accountId,
+            const std::string& conversationId,
+            std::map<std::string, std::string> message) {
+            if (accountId == aliceId && conversationId == convId && message["type"] == "member") {
+                memberMessageGenerated = true;
+                cv.notify_one();
+            }
+        }));
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::AccountMessageStatusChanged>(
+            [&](const std::string& accountId,
+                const std::string& conversationId,
+                const std::string& peer,
+                const std::string& msgId,
+                int status) {
+                if (accountId == bobId && conversationId == convId && msgId == conversationId
+                    && peer == aliceUri && status == 3) {
+                    msgDisplayed = true;
+                    cv.notify_one();
+                }
+            }));
+    DRing::registerSignalHandlers(confHandlers);
+    aliceAccount->addConversationMember(convId, bobUri);
+    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
+        return requestReceived && memberMessageGenerated;
+    }));
+    memberMessageGenerated = false;
+    bobAccount->acceptConversationRequest(convId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; }));
+
+    // Last displayed messages should not be set yet
+    auto membersInfos = aliceAccount->getConversationMembers(convId);
+    CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
+                                membersInfos.end(),
+                                [&](auto infos) {
+                                    return infos["uri"] == aliceUri && infos["lastDisplayed"] == "";
+                                })
+                   != membersInfos.end());
+
+    aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3);
+    // Bob should not receive anything here, as sendMessageDisplayed is disabled for Alice
+    CPPUNIT_ASSERT(!cv.wait_for(lk, std::chrono::seconds(10), [&]() { return msgDisplayed; }));
+
+    // Assert that message is set as displayed for self (for the read status)
+    membersInfos = aliceAccount->getConversationMembers(convId);
+    CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
+                                membersInfos.end(),
+                                [&](auto infos) {
+                                    return infos["uri"] == aliceUri
+                                           && infos["lastDisplayed"] == convId;
+                                })
+                   != membersInfos.end());
+    DRing::unregisterSignalHandlers();
+}
+
 void
 ConversationTest::testRemoveMember()
 {