diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 796661a13f2956709b51b939f8c4af774cb2307a..20f7f46fc5324a75d867cbd3601db4ce65ac1ba5 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -575,6 +575,15 @@
             </tp:docstring>
         </method>
 
+        <method name="updateProfile" tp:name-for-bindings="updateProfile">
+            <arg type="s" name="accountId" direction="in"/>
+            <arg type="s" name="displayName" direction="in"/>
+            <arg type="s" name="avatarPath" direction="in"/>
+            <tp:docstring>
+               Update the profile of the account and send it to peers.
+            </tp:docstring>
+        </method>
+
         <method name="getMessageStatus" tp:name-for-bindings="getMessageStatus">
             <arg type="t" name="id" direction="in"/>
             <arg type="i" name="status" direction="out">
diff --git a/bin/dbus/dbusconfigurationmanager.hpp b/bin/dbus/dbusconfigurationmanager.hpp
index 7363f1740f43d2f25cae5eabb5eaaa0694775898..797cb5446ccf9b8d29d037a6fa2c5b13127c7882 100644
--- a/bin/dbus/dbusconfigurationmanager.hpp
+++ b/bin/dbus/dbusconfigurationmanager.hpp
@@ -216,6 +216,12 @@ public:
         return libjami::getNearbyPeers(accountID);
     }
 
+    void
+    updateProfile(const std::string& accountID,const std::string& displayName, const std::string& avatarPath)
+    {
+        libjami::updateProfile(accountID,displayName, avatarPath);
+    }
+
     auto
     getMessageStatus(const uint64_t& id)
         -> decltype(libjami::getMessageStatus(id))
diff --git a/bin/jni/configurationmanager.i b/bin/jni/configurationmanager.i
index ca208c8923e93f4d291dc168e8228cefd38b1fb6..dcbfd80000651e67de5c47e743b411b95247323c 100644
--- a/bin/jni/configurationmanager.i
+++ b/bin/jni/configurationmanager.i
@@ -90,6 +90,7 @@ std::vector<std::map<std::string, std::string>> getConnectionList(const std::str
 std::vector<std::map<std::string, std::string>> getChannelList(const std::string& accountId, const std::string& connectionId);
 std::string addAccount(const std::map<std::string, std::string>& details);
 void removeAccount(const std::string& accountId);
+void updateProfile(const std::string& accountId,const std::string& displayName, const std::string& avatarPath);
 std::vector<std::string> getAccountList();
 void sendRegister(const std::string& accountId, bool enable);
 void registerAllAccounts(void);
diff --git a/bin/nodejs/configurationmanager.i b/bin/nodejs/configurationmanager.i
index e6b5e16edc76bbedf014a48f92d9b985c007827e..dc9c704e0e51adbd702dcef1b03fbdc94949e68e 100644
--- a/bin/nodejs/configurationmanager.i
+++ b/bin/nodejs/configurationmanager.i
@@ -86,6 +86,7 @@ std::vector<std::map<std::string, std::string>> getConnectionList(const std::str
 std::vector<std::map<std::string, std::string>> getChannelList(const std::string& accountId, const std::string& connectionId);
 std::string addAccount(const std::map<std::string, std::string>& details);
 void removeAccount(const std::string& accountId);
+void updateProfile(const std::string& accountId,const std::string& displayName, const std::string& avatarPath);
 std::vector<std::string> getAccountList();
 void sendRegister(const std::string& accountId, bool enable);
 void registerAllAccounts(void);
diff --git a/src/account.h b/src/account.h
index d2dd9bcaf4b8065027a6e9cc2eb5159145b64af3..02861e97b1f30b1aaed4a25f790d41f04fa57a42 100644
--- a/src/account.h
+++ b/src/account.h
@@ -208,6 +208,8 @@ public:
 
     virtual std::map<std::string, std::string> getNearbyPeers() const { return {}; }
 
+    virtual void updateProfile(const std::string& /*displayName*/,  const std::string& /*avatarPath*/) {}
+
     /**
      * Return the status corresponding to the token.
      */
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index 1997a8467aa15489c9e64cba53cf25da7f033c44..a7076a5bd42811ef438fa6ff56745a7d4081173f 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -293,6 +293,12 @@ getNearbyPeers(const std::string& accountId)
     return jami::Manager::instance().getNearbyPeers(accountId);
 }
 
+void
+updateProfile(const std::string& accountId,const std::string& displayName, const std::string& avatarPath)
+{
+    jami::Manager::instance().updateProfile(accountId, displayName, avatarPath);
+}
+
 int
 getMessageStatus(uint64_t messageId)
 {
diff --git a/src/jami/configurationmanager_interface.h b/src/jami/configurationmanager_interface.h
index c53cdf3a231f9a874a8e3f44674070bd8b3562d9..d49dd0c10121c0bbdac0ac38cc8e89db717b1626 100644
--- a/src/jami/configurationmanager_interface.h
+++ b/src/jami/configurationmanager_interface.h
@@ -104,6 +104,7 @@ LIBJAMI_PUBLIC bool cancelMessage(const std::string& accountId, uint64_t message
 LIBJAMI_PUBLIC std::vector<Message> getLastMessages(const std::string& accountId,
                                                     const uint64_t& base_timestamp);
 LIBJAMI_PUBLIC std::map<std::string, std::string> getNearbyPeers(const std::string& accountId);
+LIBJAMI_PUBLIC void updateProfile(const std::string& accountId,const std::string& displayName, const std::string& avatarPath);
 LIBJAMI_PUBLIC int getMessageStatus(uint64_t id);
 LIBJAMI_PUBLIC int getMessageStatus(const std::string& accountId, uint64_t id);
 LIBJAMI_PUBLIC void setIsComposing(const std::string& accountId,
diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp
index 93a58a9e590f55da97d80935c7afecb940a2ae5b..c6f4a2a091cc6e56ff1b2b456088e86e35eb448d 100644
--- a/src/jamidht/conversationrepository.cpp
+++ b/src/jamidht/conversationrepository.cpp
@@ -3903,7 +3903,7 @@ ConversationRepository::updateInfos(const std::map<std::string, std::string>& pr
     }
 
     auto addKey = [&](auto property, auto key) {
-        auto it = infosMap.find(key);
+        auto it = infosMap.find(std::string(key));
         if (it != infosMap.end()) {
             file << property;
             file << ":";
@@ -3922,7 +3922,7 @@ ConversationRepository::updateInfos(const std::map<std::string, std::string>& pr
     file << vCard::Property::PHOTO;
     file << vCard::Delimiter::SEPARATOR_TOKEN;
     file << vCard::Property::BASE64;
-    auto avatarIt = infosMap.find(vCard::Value::AVATAR);
+    auto avatarIt = infosMap.find(std::string(vCard::Value::AVATAR));
     if (avatarIt != infosMap.end()) {
         // TODO type=png? store another way?
         file << ":";
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 10213070ae6e752fa2ca3cc3ff40979d59c4450f..f0c1b259c8c159e560f33fe888d991eb21e3fdf8 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -3373,6 +3373,87 @@ JamiAccount::getNearbyPeers() const
     return discoveredPeerMap_;
 }
 
+void
+JamiAccount::sendProfileToPeers()
+{
+    if (!connectionManager_)
+        return;
+    const auto& accountUri = accountManager_->getInfo()->accountId;
+    for (const auto& connection : connectionManager_->getConnectionList()) {
+        const auto& device = connection.at("device");
+        const auto& peer = connection.at("peer");
+        if(peer == accountUri){
+            sendProfile("", accountUri, device);
+            continue;
+        }
+        const auto& conversationId = convModule()->getOneToOneConversation(peer);
+        if (!conversationId.empty()) {
+            sendProfile(conversationId, peer, device);
+        }
+    }
+}
+
+std::map<std::string, std::string>
+JamiAccount::getProfileVcard() const {
+    const auto& path = idPath_ / "profile.vcf";
+
+    if (!std::filesystem::exists(path)) {
+        return {};
+    }
+
+    return vCard::utils::toMap(fileutils::loadTextFile(path));
+}
+
+void
+JamiAccount::updateProfile(const std::string& displayName, const std::filesystem::path& avatarPath){
+
+    const auto& accountUri = accountManager_->getInfo()->accountId;
+    const auto& path = profilePath();
+    const auto& vCardPath = idPath_ / "profiles" / fmt::format("{}.vcf", base64::encode(accountUri));
+    const std::filesystem::path& tmpPath = vCardPath.string() + ".tmp";
+
+    auto profile = getProfileVcard();
+    if(profile.empty()){
+        profile = vCard::utils::initVcard();
+    }
+
+    profile["FN"] = displayName;
+    editConfig([&](JamiAccountConfig& config) { config.displayName = displayName; });
+    emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(),
+                                                                    getAccountDetails());
+    if(std::filesystem::exists(avatarPath)){
+        try {
+            const auto& base64 = jami::base64::encode(fileutils::loadFile(avatarPath));
+            profile["PHOTO;ENCODING=BASE64;TYPE=PNG"] = base64;
+        } catch (const std::exception& e) {
+            JAMI_ERROR("Failed to load avatar: {}", e.what());
+        }
+    }else if(avatarPath.empty()){
+        profile["PHOTO;ENCODING=BASE64;TYPE=PNG"] = "";
+    }
+    // nothing happens to the profile photo if the avatarPath is invalid
+    // and not empty. So far it seems to be the best default behavior.
+
+    const std::string& vCard = vCard::utils::toString(profile);
+
+    try {
+        std::ofstream file(tmpPath);
+        if (file.is_open()) {
+            file << vCard;
+            file.close();
+            std::filesystem::rename(tmpPath, vCardPath);
+            fileutils::createFileLink(path,vCardPath);
+            sendProfileToPeers();
+            emitSignal<libjami::ConfigurationSignal::ProfileReceived>(getAccountID(), accountUri, path.string());
+        } else {
+            JAMI_ERROR("Unable to open file for writing: {}", tmpPath.string());
+        }
+    } catch (const std::exception& e) {
+        JAMI_ERROR("Error writing profile: {}", e.what());
+    }
+}
+
+
 void
 JamiAccount::setActiveCodecs(const std::vector<unsigned>& list)
 {
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index a038893b6e524a58660554468a23fff3aca8bb3d..d5d0fa9bc6c803b90b1117736360caf180721b2e 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -420,6 +420,10 @@ public:
      */
     std::map<std::string, std::string> getNearbyPeers() const override;
 
+    std::map<std::string, std::string> getProfileVcard() const;
+    void sendProfileToPeers();
+    void updateProfile(const std::string& displayName, const std::filesystem::path& avatarPath);
+
 #ifdef LIBJAMI_TESTABLE
     dhtnet::ConnectionManager& connectionManager() { return *connectionManager_; }
 
diff --git a/src/manager.cpp b/src/manager.cpp
index a04b1336958819dd2509a9722e44983fde7aaaf6..d4878b97d553985563decd71bfdf791676ad34eb 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -3264,6 +3264,14 @@ Manager::getNearbyPeers(const std::string& accountID)
     return {};
 }
 
+void
+Manager::updateProfile(const std::string& accountID,const std::string& displayName, const std::string& avatarPath)
+{
+    if (const auto acc = getAccount<JamiAccount>(accountID))
+        acc->updateProfile(displayName,avatarPath);
+}
+
+
 void
 Manager::setDefaultModerator(const std::string& accountID, const std::string& peerURI, bool state)
 {
diff --git a/src/manager.h b/src/manager.h
index cc4fab98a0fa29faac9526d33a227487e7b68e33..13dc5a6c701465cbff507d1763d4e286722e8d25 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -811,6 +811,8 @@ public:
 
     std::map<std::string, std::string> getNearbyPeers(const std::string& accountID);
 
+    void updateProfile(const std::string& accountID,const std::string& displayName,const std::string& avatarPath);
+
 #ifdef ENABLE_VIDEO
     /**
      * Create a new SinkClient instance, store it in an internal cache as a weak_ptr
diff --git a/src/vcard.cpp b/src/vcard.cpp
index 4a86e45789186c1224c2f43f5bd31d04afb45b87..a9931968422e433d30b871ab43e271d72910205a 100644
--- a/src/vcard.cpp
+++ b/src/vcard.cpp
@@ -37,6 +37,45 @@ toMap(std::string_view content)
     }
     return vCard;
 }
+
+std::map<std::string, std::string>
+initVcard()
+{
+    return {
+        {std::string(Property::VCARD_VERSION), "2.1"},
+        {std::string(Property::FORMATTED_NAME), ""},
+        {std::string(Property::PHOTO_PNG), ""},
+    };
+}
+
+
+std::string
+toString(const std::map<std::string, std::string>& vCard)
+{
+    size_t estimatedSize = 0;
+    for (const auto& [key, value] : vCard) {
+        if (Delimiter::BEGIN_TOKEN_KEY == key || Delimiter::END_TOKEN_KEY == key)
+            continue;
+        estimatedSize += key.size() + value.size() + 2;
+    }
+    std::string result;
+    result.reserve(estimatedSize + Delimiter::BEGIN_TOKEN.size() + Delimiter::END_LINE_TOKEN.size() + Delimiter::END_TOKEN.size() + Delimiter::END_LINE_TOKEN.size());
+
+    result += Delimiter::BEGIN_TOKEN;
+    result += Delimiter::END_LINE_TOKEN;
+
+    for (const auto& [key, value] : vCard) {
+        if (Delimiter::BEGIN_TOKEN_KEY == key || Delimiter::END_TOKEN_KEY == key)
+            continue;
+        result += key + ':' + value + '\n';
+    }
+
+    result += Delimiter::END_TOKEN;
+    result += Delimiter::END_LINE_TOKEN;
+
+    return result;
+}
+
 } // namespace utils
 
 } // namespace vCard
diff --git a/src/vcard.h b/src/vcard.h
index eb13c7473834e39a82b778a72b9e55d64d1e01eb..322e779269888d0717a722f6cae4ed51fffb9cb3 100644
--- a/src/vcard.h
+++ b/src/vcard.h
@@ -24,59 +24,61 @@ namespace vCard {
 
 struct Delimiter
 {
-    constexpr static const char* SEPARATOR_TOKEN = ";";
-    constexpr static const char* END_LINE_TOKEN = "\n";
-    constexpr static const char* BEGIN_TOKEN = "BEGIN:VCARD";
-    constexpr static const char* END_TOKEN = "END:VCARD";
-};
+    constexpr static std::string_view SEPARATOR_TOKEN = ";";
+    constexpr static std::string_view END_LINE_TOKEN = "\n";
+    constexpr static std::string_view BEGIN_TOKEN = "BEGIN:VCARD";
+    constexpr static std::string_view END_TOKEN = "END:VCARD";
+    constexpr static std::string_view BEGIN_TOKEN_KEY = "BEGIN";
+    constexpr static std::string_view END_TOKEN_KEY = "END";
+};;
 
 struct Property
 {
-    constexpr static const char* UID = "UID";
-    constexpr static const char* VCARD_VERSION = "VERSION";
-    constexpr static const char* ADDRESS = "ADR";
-    constexpr static const char* AGENT = "AGENT";
-    constexpr static const char* BIRTHDAY = "BDAY";
-    constexpr static const char* CATEGORIES = "CATEGORIES";
-    constexpr static const char* CLASS = "CLASS";
-    constexpr static const char* DELIVERY_LABEL = "LABEL";
-    constexpr static const char* EMAIL = "EMAIL";
-    constexpr static const char* FORMATTED_NAME = "FN";
-    constexpr static const char* GEOGRAPHIC_POSITION = "GEO";
-    constexpr static const char* KEY = "KEY";
-    constexpr static const char* LOGO = "LOGO";
-    constexpr static const char* MAILER = "MAILER";
-    constexpr static const char* NAME = "N";
-    constexpr static const char* NICKNAME = "NICKNAME";
-    constexpr static const char* DESCRIPTION = "DESCRIPTION";
-    constexpr static const char* NOTE = "NOTE";
-    constexpr static const char* ORGANIZATION = "ORG";
-    constexpr static const char* PHOTO = "PHOTO";
-    constexpr static const char* PRODUCT_IDENTIFIER = "PRODID";
-    constexpr static const char* REVISION = "REV";
-    constexpr static const char* ROLE = "ROLE";
-    constexpr static const char* SORT_STRING = "SORT-STRING";
-    constexpr static const char* SOUND = "SOUND";
-    constexpr static const char* TELEPHONE = "TEL";
-    constexpr static const char* TIME_ZONE = "TZ";
-    constexpr static const char* TITLE = "TITLE";
-    constexpr static const char* RDV_ACCOUNT = "RDV_ACCOUNT";
-    constexpr static const char* RDV_DEVICE = "RDV_DEVICE";
-    constexpr static const char* URL = "URL";
-    constexpr static const char* BASE64 = "ENCODING=BASE64";
-    constexpr static const char* TYPE_PNG = "TYPE=PNG";
-    constexpr static const char* TYPE_JPEG = "TYPE=JPEG";
-    constexpr static const char* PHOTO_PNG = "PHOTO;ENCODING=BASE64;TYPE=PNG";
-    constexpr static const char* PHOTO_JPEG = "PHOTO;ENCODING=BASE64;TYPE=JPEG";
+    constexpr static std::string_view UID = "UID";
+    constexpr static std::string_view VCARD_VERSION = "VERSION";
+    constexpr static std::string_view ADDRESS = "ADR";
+    constexpr static std::string_view AGENT = "AGENT";
+    constexpr static std::string_view BIRTHDAY = "BDAY";
+    constexpr static std::string_view CATEGORIES = "CATEGORIES";
+    constexpr static std::string_view CLASS = "CLASS";
+    constexpr static std::string_view DELIVERY_LABEL = "LABEL";
+    constexpr static std::string_view EMAIL = "EMAIL";
+    constexpr static std::string_view FORMATTED_NAME = "FN";
+    constexpr static std::string_view GEOGRAPHIC_POSITION = "GEO";
+    constexpr static std::string_view KEY = "KEY";
+    constexpr static std::string_view LOGO = "LOGO";
+    constexpr static std::string_view MAILER = "MAILER";
+    constexpr static std::string_view NAME = "N";
+    constexpr static std::string_view NICKNAME = "NICKNAME";
+    constexpr static std::string_view DESCRIPTION = "DESCRIPTION";
+    constexpr static std::string_view NOTE = "NOTE";
+    constexpr static std::string_view ORGANIZATION = "ORG";
+    constexpr static std::string_view PHOTO = "PHOTO";
+    constexpr static std::string_view PRODUCT_IDENTIFIER = "PRODID";
+    constexpr static std::string_view REVISION = "REV";
+    constexpr static std::string_view ROLE = "ROLE";
+    constexpr static std::string_view SORT_STRING = "SORT-STRING";
+    constexpr static std::string_view SOUND = "SOUND";
+    constexpr static std::string_view TELEPHONE = "TEL";
+    constexpr static std::string_view TIME_ZONE = "TZ";
+    constexpr static std::string_view TITLE = "TITLE";
+    constexpr static std::string_view RDV_ACCOUNT = "RDV_ACCOUNT";
+    constexpr static std::string_view RDV_DEVICE = "RDV_DEVICE";
+    constexpr static std::string_view URL = "URL";
+    constexpr static std::string_view BASE64 = "ENCODING=BASE64";
+    constexpr static std::string_view TYPE_PNG = "TYPE=PNG";
+    constexpr static std::string_view TYPE_JPEG = "TYPE=JPEG";
+    constexpr static std::string_view PHOTO_PNG = "PHOTO;ENCODING=BASE64;TYPE=PNG";
+    constexpr static std::string_view PHOTO_JPEG = "PHOTO;ENCODING=BASE64;TYPE=JPEG";
 };
 
 struct Value
 {
-    constexpr static const char* TITLE = "title";
-    constexpr static const char* DESCRIPTION = "description";
-    constexpr static const char* AVATAR = "avatar";
-    constexpr static const char* RDV_ACCOUNT = "rdvAccount";
-    constexpr static const char* RDV_DEVICE = "rdvDevice";
+    constexpr static std::string_view TITLE = "title";
+    constexpr static std::string_view DESCRIPTION = "description";
+    constexpr static std::string_view AVATAR = "avatar";
+    constexpr static std::string_view RDV_ACCOUNT = "rdvAccount";
+    constexpr static std::string_view RDV_DEVICE = "rdvDevice";
 };
 
 namespace utils {
@@ -86,6 +88,9 @@ namespace utils {
  * @return the vCard representation
  */
 std::map<std::string, std::string> toMap(std::string_view content);
+std::map<std::string, std::string> initVcard();
+std::string toString(const std::map<std::string, std::string>& vCard);
+
 } // namespace utils
 
 } // namespace vCard