diff --git a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
index 5dd8e9d7fe0cd5bb3a955ea4892d5864b89e2527..cacbb47e8cf484172fa811bc264c8877b3d2254f 100644
--- a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
+++ b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
@@ -154,5 +154,16 @@
             </arg>
         </method>
 
+       <method name="addValueToPreference" tp:name-for-bindings="addValueToPreference">
+            <tp:added version="9.6.0"/>
+            <arg type="s" name="pluginId" direction="in">
+            </arg>
+            <arg type="s" name="preferenceKey" direction="in">
+            </arg>
+            <arg type="s" name="value" direction="in">
+            </arg>
+            <arg type="b" name="status" direction="out">
+            </arg>
+        </method>
     </interface>
 </node>
diff --git a/bin/dbus/dbuspluginmanagerinterface.cpp b/bin/dbus/dbuspluginmanagerinterface.cpp
index 9bc0b098cb91ab7b36c427fe2a0eac5a1d9a56c1..8b63ed47057f19f8a73d0c98fa55023150090707 100644
--- a/bin/dbus/dbuspluginmanagerinterface.cpp
+++ b/bin/dbus/dbuspluginmanagerinterface.cpp
@@ -132,3 +132,11 @@ DBusPluginManagerInterface::getCallMediaHandlerStatus()
 {
     return DRing::getCallMediaHandlerStatus();
 }
+
+bool
+DBusPluginManagerInterface::addValueToPreference(const std::string& pluginId,
+                          const std::string& preferenceKey,
+                          const std::string& value)
+{
+    return DRing::addValueToPreference(pluginId, preferenceKey, value);
+}
diff --git a/bin/dbus/dbuspluginmanagerinterface.h b/bin/dbus/dbuspluginmanagerinterface.h
index 9fd353660adf5e6ca5bf359b6ab48e26052fd8a6..4ebaeb6dbd9f7fe09b03493afb1882ad5e025f68 100644
--- a/bin/dbus/dbuspluginmanagerinterface.h
+++ b/bin/dbus/dbuspluginmanagerinterface.h
@@ -71,4 +71,7 @@ class DRING_PUBLIC DBusPluginManagerInterface :
         bool getPluginsEnabled();
         void setPluginsEnabled(const bool& state);
         std::map<std::string,std::string> getCallMediaHandlerStatus();
+        bool addValueToPreference(const std::string& pluginId,
+                                  const std::string& preferenceKey,
+                                  const std::string& value);
 };
diff --git a/bin/jni/plugin_manager_interface.i b/bin/jni/plugin_manager_interface.i
index 330c5c8dd54765a328dc66ae42f57909c28d945c..d995f67d8f1171e4e76b7aede1e82338f4075d71 100644
--- a/bin/jni/plugin_manager_interface.i
+++ b/bin/jni/plugin_manager_interface.i
@@ -42,4 +42,7 @@ std::map<std::string,std::string> getCallMediaHandlerDetails(const std::string&
 bool getPluginsEnabled();
 void setPluginsEnabled(bool state);
 std::map<std::string,std::string> getCallMediaHandlerStatus();
+bool addValueToPreference(const std::string& pluginId,
+                          const std::string& preferenceKey,
+                          const std::string& value);
 }
diff --git a/configure.ac b/configure.ac
index 2efeab8971d40ead044420c0b2bb10da01c19941..65e785d1cf0537fcd24d3d970a05329cf9b655db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Jami - configure.ac for automake 1.9 and autoconf 2.59
 
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.65])
-AC_INIT([Jami Daemon],[9.5.0],[ring@gnu.org],[jami])
+AC_INIT([Jami Daemon],[9.6.0],[ring@gnu.org],[jami])
 
 AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2020]])
 AC_REVISION([$Revision$])
diff --git a/meson.build b/meson.build
index 9f49b57ba3e78382bc5c56466766372f870db601..86b0643e0939b7242fd342b9ecb79e1292d08451 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('jami-daemon', ['c', 'cpp'],
-        version: '9.5.0',
+        version: '9.6.0',
         license: 'GPL3+',
         default_options: ['cpp_std=gnu++17', 'buildtype=debugoptimized'],
         meson_version:'>= 0.54'
diff --git a/src/client/plugin_manager_interface.cpp b/src/client/plugin_manager_interface.cpp
index 3e28ac6ca18f6529375ae2c91cc8e49e6d94b1a9..97acea27728361e16b86f0eecbd28c9e814f2a4e 100644
--- a/src/client/plugin_manager_interface.cpp
+++ b/src/client/plugin_manager_interface.cpp
@@ -144,4 +144,14 @@ getCallMediaHandlerStatus()
         .getCallServicesManager()
         .getCallMediaHandlerStatus();
 }
+
+bool
+addValueToPreference(const std::string& pluginId,
+                     const std::string& preferenceKey,
+                     const std::string& value)
+{
+    return jami::Manager::instance().getJamiPluginManager().addValueToPreference(pluginId,
+                                                                                 preferenceKey,
+                                                                                 value);
+}
 } // namespace DRing
diff --git a/src/dring/plugin_manager_interface.h b/src/dring/plugin_manager_interface.h
index e99a5459f1cd063508ab5ce7f464b95e094a8429..01570212d82177b63f546e8cf32a4ae35cd1aaa7 100644
--- a/src/dring/plugin_manager_interface.h
+++ b/src/dring/plugin_manager_interface.h
@@ -53,4 +53,7 @@ DRING_PUBLIC std::map<std::string,std::string> getCallMediaHandlerDetails(const
 DRING_PUBLIC bool getPluginsEnabled();
 DRING_PUBLIC void setPluginsEnabled(bool state);
 DRING_PUBLIC std::map<std::string,std::string> getCallMediaHandlerStatus();
+DRING_PUBLIC bool addValueToPreference(const std::string& pluginId,
+                                       const std::string& preferenceKey,
+                                       const std::string& value);
 }
diff --git a/src/fileutils.cpp b/src/fileutils.cpp
index 81e2290cf98c898d01c56b52c005fdf66c1fe3ff..b1576fba85018ee5c7445213aba661fe674ff516 100644
--- a/src/fileutils.cpp
+++ b/src/fileutils.cpp
@@ -1023,5 +1023,35 @@ accessFile(const std::string& file, int mode)
 #endif
 }
 
+std::string
+getFileName(const std::string& filePath)
+{
+    std::string fileName = filePath;
+    const size_t last_slash_idx = fileName.find_last_of(DIR_SEPARATOR_STR_ESC);
+    if (std::string::npos != last_slash_idx) {
+        fileName.erase(0, last_slash_idx + 1);
+    }
+    return fileName;
+}
+
+std::string
+removeExtension(const std::string& filePath)
+{
+    std::string fileName = filePath;
+    const size_t period_idx = fileName.rfind('.');
+    if (std::string::npos != period_idx) {
+        fileName.erase(period_idx);
+    }
+    return fileName;
+}
+
+std::string
+getExtension(const std::string& filePath)
+{
+    std::string fileExt = filePath;
+    fileExt = fileExt.substr(fileExt.find_last_of('.'));
+    return fileExt;
+}
+
 } // namespace jami
 } // namespace fileutils
diff --git a/src/fileutils.h b/src/fileutils.h
index 57e424c31eb5a34a54ec3b399b76093f440068e5..9f1c54a52be8f0d4a83eb51397344d3de9a519de 100644
--- a/src/fileutils.h
+++ b/src/fileutils.h
@@ -176,6 +176,10 @@ std::string md5sum(const std::vector<uint8_t>& buffer);
  */
 int accessFile(const std::string& file, int mode);
 
+std::string getFileName(const std::string& filePath);
+std::string removeExtension(const std::string& filePath);
+std::string getExtension(const std::string& filePath);
+
 } // namespace fileutils
 } // namespace jami
 
diff --git a/src/plugin/jamipluginmanager.cpp b/src/plugin/jamipluginmanager.cpp
index 69d84e403d7ff73246fce96bb16d471025506277..dd16401e527056f5442ac7ca86fd588fd93e5448 100644
--- a/src/plugin/jamipluginmanager.cpp
+++ b/src/plugin/jamipluginmanager.cpp
@@ -34,6 +34,7 @@ extern "C" {
 #include <archive.h>
 }
 
+#include "fileutils.h"
 #include <json/json.h>
 #include <msgpack.hpp>
 
@@ -337,11 +338,10 @@ JamiPluginManager::unloadPlugin(const std::string& rootPath)
 void
 JamiPluginManager::togglePlugin(const std::string& rootPath, bool toggle)
 {
-    //This function should not be used as is
-    //One should modify it to perform plugin install followed by load
-    //rootPath should be the jplpath!
-    try
-    {
+    // This function should not be used as is
+    // One should modify it to perform plugin install followed by load
+    // rootPath should be the jplpath!
+    try {
         std::string soPath = getPluginDetails(rootPath).at("soPath");
         // remove the previous plugin object if it was registered
         pm_.destroyPluginComponents(soPath);
@@ -372,6 +372,8 @@ std::vector<std::map<std::string, std::string>>
 JamiPluginManager::getPluginPreferences(const std::string& rootPath)
 {
     const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath);
+    std::map<std::string, std::map<std::string, std::string>> userPreferences
+        = getUserPreferencesValuesMap(rootPath);
     std::ifstream file(preferenceFilePath);
     Json::Value root;
     Json::CharReaderBuilder rbuilder;
@@ -389,11 +391,34 @@ JamiPluginManager::getPluginPreferences(const std::string& rootPath)
                 std::string key = jsonPreference.get("key", "None").asString();
                 if (type != "None" && key != "None") {
                     if (keys.find(key) == keys.end()) {
-                        const auto& preferenceAttributes = parsePreferenceConfig(jsonPreference,
-                                                                                 type);
+                        std::map<std::string, std::string> preferenceAttributes
+                            = parsePreferenceConfig(jsonPreference, type);
                         // If the parsing of the attributes was successful, commit the map and the key
                         if (!preferenceAttributes.empty()) {
-                            preferences.push_back(std::move(preferenceAttributes));
+                            if (!userPreferences[key].empty()) {
+                                preferenceAttributes["entryValues"]
+                                    = userPreferences[key]["entryValues"];
+                                preferenceAttributes["entries"] = userPreferences[key]["entries"];
+                            }
+
+                            preferenceAttributes["entryValues"]
+                                = std::regex_replace(preferenceAttributes["entryValues"],
+                                                     std::regex("\\["),
+                                                     "$2");
+                            preferenceAttributes["entryValues"]
+                                = std::regex_replace(preferenceAttributes["entryValues"],
+                                                     std::regex("\\]"),
+                                                     "$2");
+                            preferenceAttributes["entries"]
+                                = std::regex_replace(preferenceAttributes["entries"],
+                                                     std::regex("\\["),
+                                                     "$2");
+                            preferenceAttributes["entries"]
+                                = std::regex_replace(preferenceAttributes["entries"],
+                                                     std::regex("\\]"),
+                                                     "$2");
+
+                            preferences.emplace_back(std::move(preferenceAttributes));
                             keys.insert(key);
                         }
                     }
@@ -443,6 +468,41 @@ JamiPluginManager::getPluginUserPreferencesValuesMap(const std::string& rootPath
     return rmap;
 }
 
+std::map<std::string, std::map<std::string, std::string>>
+JamiPluginManager::getUserPreferencesValuesMap(const std::string& rootPath)
+{
+    const std::string preferencesValuesFilePath = pluginAddedPreferencesValuesFilePath(rootPath);
+    std::ifstream file(preferencesValuesFilePath, std::ios::binary);
+    std::map<std::string, std::map<std::string, std::string>> rmap;
+
+    // If file is accessible
+    if (file.good()) {
+        std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
+        // Get file size
+        std::string str;
+        file.seekg(0, std::ios::end);
+        size_t fileSize = static_cast<size_t>(file.tellg());
+        // If not empty
+        if (fileSize > 0) {
+            // Read whole file content and put it in the string str
+            str.reserve(static_cast<size_t>(file.tellg()));
+            file.seekg(0, std::ios::beg);
+            str.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+            file.close();
+            try {
+                // Unpack the string
+                msgpack::object_handle oh = msgpack::unpack(str.data(), str.size());
+                // Deserialized object is valid during the msgpack::object_handle instance is alive.
+                msgpack::object deserialized = oh.get();
+                deserialized.convert(rmap);
+            } catch (const std::exception& e) {
+                JAMI_ERR() << e.what();
+            }
+        }
+    }
+    return rmap;
+}
+
 bool
 JamiPluginManager::setPluginPreference(const std::string& rootPath,
                                        const std::string& key,
@@ -521,6 +581,69 @@ JamiPluginManager::resetPluginPreferencesValuesMap(const std::string& rootPath)
     return returnValue;
 }
 
+bool
+JamiPluginManager::copyFileToPluginData(const std::string& pluginId,
+                                        const std::string& value,
+                                        const std::string& preferenceCategory,
+                                        std::string& fileName,
+                                        std::string& fileExt)
+{
+    if (!fileutils::isFile(value))
+        return false;
+
+    const std::string destinationDir {pluginId + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH
+                                      + preferenceCategory + DIR_SEPARATOR_CH};
+    fileName = fileutils::removeExtension(fileutils::getFileName(value));
+    fileExt = fileutils::getExtension(value);
+
+    auto srcData = fileutils::loadFile(value);
+
+    if (fileutils::isFile(destinationDir + fileName + fileExt)) {
+        fileutils::saveFile(destinationDir + fileName + fileExt, srcData);
+        return false;
+    }
+    fileutils::saveFile(destinationDir + fileName + fileExt, srcData);
+    return true;
+}
+
+bool
+JamiPluginManager::addValueToPreference(const std::string& pluginId,
+                                        const std::string& preferenceKey,
+                                        const std::string& value)
+{
+    std::map<std::string, std::map<std::string, std::string>> userPreferences
+        = getUserPreferencesValuesMap(pluginId);
+    std::vector<std::map<std::string, std::string>> preferences = getPluginPreferences(pluginId);
+
+    for (auto& preference : preferences) {
+        if (preference["key"] == preferenceKey) {
+            std::string fileName, fileExt;
+            if (!copyFileToPluginData(pluginId, value, preference["category"], fileName, fileExt)) {
+                return setPluginPreference(pluginId, preferenceKey, fileName + fileExt);
+            }
+            setPluginPreference(pluginId, preferenceKey, fileName + fileExt);
+            userPreferences[preferenceKey]["entries"] = preference["entries"] + "," + fileName;
+            userPreferences[preferenceKey]["entryValues"] = preference["entryValues"] + ","
+                                                            + fileName + fileExt;
+
+            const std::string preferencesValuesFilePath = pluginAddedPreferencesValuesFilePath(
+                pluginId);
+            std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
+            if (!fs.good()) {
+                return false;
+            }
+            try {
+                std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
+                msgpack::pack(fs, userPreferences);
+                return true;
+            } catch (const std::exception& e) {
+                JAMI_ERR() << e.what();
+                return false;
+            }
+        }
+    }
+}
+
 std::map<std::string, std::string>
 JamiPluginManager::readPluginManifestFromArchive(const std::string& jplPath)
 {
diff --git a/src/plugin/jamipluginmanager.h b/src/plugin/jamipluginmanager.h
index fb37398d38c75e3450c9168ab104088e583834ae..d3c9ad15843ef983c24d6090938efcef99efb78d 100644
--- a/src/plugin/jamipluginmanager.h
+++ b/src/plugin/jamipluginmanager.h
@@ -125,6 +125,10 @@ public:
 
     bool resetPluginPreferencesValuesMap(const std::string& rootPath);
 
+    bool addValueToPreference(const std::string& pluginId,
+                              const std::string& preferenceKey,
+                              const std::string& value);
+
 public:
     CallServicesManager& getCallServicesManager() { return csm_; }
 
@@ -180,6 +184,13 @@ private:
     }
 
     std::map<std::string, std::string> getPluginUserPreferencesValuesMap(const std::string& rootPath);
+    std::map<std::string, std::map<std::string, std::string>> getUserPreferencesValuesMap(
+        const std::string& rootPath);
+    bool copyFileToPluginData(const std::string& pluginId,
+                              const std::string& value,
+                              const std::string& preferenceCategory,
+                              std::string& fileName,
+                              std::string& fileExt);
 
     /**
      * @brief getPreferencesConfigFilePath
@@ -205,6 +216,18 @@ private:
         return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack";
     }
 
+    /**
+     * @brief pluginAddedPreferencesValuesFilePath
+     * Returns the plugin added preferences values file path from the plugin root path
+     * This is entirely defined by how the plugin files are structured
+     * @param plugin rootPath
+     * @return path of the preferences values
+     */
+    std::string pluginAddedPreferencesValuesFilePath(const std::string& rootPath) const
+    {
+        return rootPath + DIR_SEPARATOR_CH + "addedPreferences.msgpack";
+    }
+
     void registerServices();
 
 private:
diff --git a/src/plugin/mediahandler.h b/src/plugin/mediahandler.h
index 422a7991348f13d31512194dfe49ab427ba85d59..7a25457da3f8e03981ecf6663b05b702a5b0b6b2 100644
--- a/src/plugin/mediahandler.h
+++ b/src/plugin/mediahandler.h
@@ -40,8 +40,8 @@ public:
      * The id is the path of the plugin that created this MediaHandler
      * @return
      */
-    std::string id() const { return id_;}
-    virtual void setId(const std::string& id) final {id_ = id;}
+    std::string id() const { return id_; }
+    virtual void setId(const std::string& id) final { id_ = id; }
 
 private:
     std::string id_;