diff --git a/res/test_plugin/plugin/__init__.py b/res/test_plugin/plugin/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ed03dffc10489701fe7d86f740a787330d377fd
--- /dev/null
+++ b/res/test_plugin/plugin/__init__.py
@@ -0,0 +1,5 @@
+from PyQt5.QtWidgets import QMessageBox
+
+
+def display_messagebox():
+    QMessageBox.about(None, "About", "Sakia")
\ No newline at end of file
diff --git a/src/sakia/app.py b/src/sakia/app.py
index 85ab35e4427492467faaa18f54dc5cfcb225fb1c..e38c3caf828ff3cf3b4cc03ddc471df88343da9f 100644
--- a/src/sakia/app.py
+++ b/src/sakia/app.py
@@ -14,7 +14,7 @@ from sakia.data.repositories import SakiaDatabase
 from sakia.data.entities import Transaction, Connection, Identity, Dividend
 from sakia.data.processors import BlockchainProcessor, NodesProcessor, IdentitiesProcessor, \
     CertificationsProcessor, SourcesProcessor, TransactionsProcessor, ConnectionsProcessor, DividendsProcessor
-from sakia.data.files import AppDataFile, UserParametersFile
+from sakia.data.files import AppDataFile, UserParametersFile, PluginsDirectory
 from sakia.decorators import asyncify
 from sakia.money import *
 import asyncio
@@ -60,6 +60,7 @@ class Application(QObject):
     parameters = attr.ib()
     db = attr.ib()
     currency = attr.ib()
+    plugins_dir = attr.ib()
     network_service = attr.ib(default=None)
     blockchain_service = attr.ib(default=None)
     identities_service = attr.ib(default=None)
@@ -81,7 +82,7 @@ class Application(QObject):
         qapp.setAttribute(Qt.AA_EnableHighDpiScaling, True)
         options = SakiaOptions.from_arguments(argv)
         app_data = AppDataFile.in_config_path(options.config_path).load_or_init()
-        app = cls(qapp, loop, options, app_data, None, None, options.currency)
+        app = cls(qapp, loop, options, app_data, None, None, options.currency, None)
         #app.set_proxy()
         app.get_last_version()
         app.load_profile(app_data.default)
@@ -96,6 +97,7 @@ class Application(QObject):
         :param profile_name:
         :return:
         """
+        self.plugins_dir = PluginsDirectory.in_config_path(self.options.config_path, profile_name).load_or_init()
         self.parameters = UserParametersFile.in_config_path(self.options.config_path, profile_name).load_or_init()
         self.db = SakiaDatabase.load_or_init(self.options, profile_name)
 
diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py
index 3224769090cbcebac2b3c5dfd960ddd2f761ec3d..40190cdb6e4e8e50ac33f73958b5ce8170dc4ad4 100644
--- a/src/sakia/data/entities/__init__.py
+++ b/src/sakia/data/entities/__init__.py
@@ -9,3 +9,4 @@ from .app_data import AppData
 from .source import Source
 from .dividend import Dividend
 from .contact import Contact
+from .plugin import Plugin
diff --git a/src/sakia/data/entities/plugin.py b/src/sakia/data/entities/plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e39b44c56b54e06a43959441cb57c67f6d971c2
--- /dev/null
+++ b/src/sakia/data/entities/plugin.py
@@ -0,0 +1,10 @@
+import attr
+
+
+@attr.s(frozen=True)
+class Plugin:
+    name = attr.ib()
+    description = attr.ib()
+    version = attr.ib()
+    imported = attr.ib()
+    module = attr.ib()
diff --git a/src/sakia/data/files/__init__.py b/src/sakia/data/files/__init__.py
index cc136098a6d0981bebaaf8a3b1fca6fc76655beb..efff6b0c2807e6a94c9a0a300b5c3db9d3841ad1 100644
--- a/src/sakia/data/files/__init__.py
+++ b/src/sakia/data/files/__init__.py
@@ -1,2 +1,3 @@
 from .user_parameters import UserParametersFile
 from .app_data import AppDataFile
+from .plugins import PluginsDirectory, Plugin
\ No newline at end of file
diff --git a/src/sakia/data/files/plugins.py b/src/sakia/data/files/plugins.py
new file mode 100644
index 0000000000000000000000000000000000000000..a909089fbe8ce6a67218eafad942e369807b9d2d
--- /dev/null
+++ b/src/sakia/data/files/plugins.py
@@ -0,0 +1,47 @@
+import attr
+import os
+import sys
+import logging
+import importlib
+from ..entities import Plugin
+
+
+@attr.s(frozen=True)
+class PluginsDirectory:
+    """
+    The repository for UserParameters
+    """
+    _path = attr.ib()
+    plugins = attr.ib(default=[])
+    _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
+
+    @classmethod
+    def in_config_path(cls, config_path, profile_name):
+        plugins_path = os.path.join(config_path, profile_name, "plugins")
+        if not os.path.exists(plugins_path):
+            os.makedirs(plugins_path)
+        return cls(plugins_path)
+
+    def load_or_init(self):
+        """
+        Init plugins
+        """
+        try:
+            for file in os.listdir(self._path):
+                if file.endswith(".zip"):
+                    sys.path.append(os.path.join(self._path, file))
+                    module_name = os.path.splitext(os.path.basename(file))[0]
+                    try:
+                        plugin_module = importlib.import_module(module_name)
+                        self.plugins.append(Plugin(plugin_module.PLUGIN_NAME,
+                                                   plugin_module.PLUGIN_DESCRIPTION,
+                                                   plugin_module.PLUGIN_VERSION,
+                                                   True,
+                                                   plugin_module))
+                    except ImportError as e:
+                        self.plugins.append(Plugin(module_name, "", "",
+                                                   False, None))
+                        self._logger.debug(str(e) + " with sys.path " + str(sys.path))
+        except OSError as e:
+            self._logger.debug(str(e))
+        return self
diff --git a/src/sakia/gui/dialogs/plugins_manager/__init__.py b/src/sakia/gui/dialogs/plugins_manager/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/sakia/gui/dialogs/plugins_manager/controller.py b/src/sakia/gui/dialogs/plugins_manager/controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..26de71c027b8d6e371a6971d7c74c1278f13c397
--- /dev/null
+++ b/src/sakia/gui/dialogs/plugins_manager/controller.py
@@ -0,0 +1,63 @@
+import asyncio
+
+from PyQt5.QtCore import QObject
+from .model import PluginsManagerModel
+from .view import PluginsManagerView
+import attr
+
+
+@attr.s()
+class PluginsManagerController(QObject):
+    """
+    The PluginManager view
+    """
+
+    view = attr.ib()
+    model = attr.ib()
+
+    def __attrs_post_init__(self):
+        super().__init__()
+        self.view.button_box.rejected.connect(self.view.close)
+
+    @classmethod
+    def create(cls, parent, app):
+        """
+        Instanciate a PluginManager component
+        :param sakia.gui.component.controller.ComponentController parent:
+        :param sakia.app.Application app: sakia application
+        :return: a new PluginManager controller
+        :rtype: PluginManagerController
+        """
+        view = PluginsManagerView(parent.view if parent else None)
+        model = PluginsManagerModel(app)
+        plugin = cls(view, model)
+        view.set_table_plugins_model(model.init_plugins_table())
+        view.button_delete.clicked.connect(plugin.delete_plugin)
+        return plugin
+
+    @classmethod
+    def open_dialog(cls, parent, app):
+        """
+        Certify and identity
+        :param sakia.gui.component.controller.ComponentController parent: the parent
+        :param sakia.core.Application app: the application
+        :param sakia.core.Account account: the account certifying the identity
+        :param sakia.core.Community community: the community
+        :return:
+        """
+        dialog = cls.create(parent, app)
+        return dialog.exec()
+
+    def delete_plugin(self):
+        plugin_index = self.view.selected_plugin_index()
+        plugin = self.model.plugin(plugin_index)
+        self.model.delete_plugin(plugin)
+
+    def async_exec(self):
+        future = asyncio.Future()
+        self.view.finished.connect(lambda r: future.set_result(r))
+        self.view.open()
+        return future
+
+    def exec(self):
+        self.view.exec()
diff --git a/src/sakia/gui/dialogs/plugins_manager/model.py b/src/sakia/gui/dialogs/plugins_manager/model.py
new file mode 100644
index 0000000000000000000000000000000000000000..52dfd84dae184e426f3ff8cf69b801b6f4bdd774
--- /dev/null
+++ b/src/sakia/gui/dialogs/plugins_manager/model.py
@@ -0,0 +1,41 @@
+from PyQt5.QtCore import QObject, Qt
+from .table_model import PluginsTableModel, PluginsFilterProxyModel
+import attr
+
+
+@attr.s()
+class PluginsManagerModel(QObject):
+    """
+    The model of Plugin component
+    """
+
+    app = attr.ib()
+
+    def __attrs_post_init__(self):
+        super().__init__()
+
+    def init_plugins_table(self):
+        """
+        Generates a plugins table model
+        :return:
+        """
+        self._model = PluginsTableModel(self, self.app)
+        self._proxy = PluginsFilterProxyModel(self)
+        self._proxy.setSourceModel(self._model)
+        self._proxy.setDynamicSortFilter(True)
+        self._proxy.setSortRole(Qt.DisplayRole)
+        self._model.init_plugins()
+        return self._proxy
+
+    def delete_plugin(self, plugin):
+        self.app.plugins_dir.uninstall_plugin(plugin)
+        self._model.remove_plugin(plugin)
+
+    def plugin(self, index):
+        plugin_name = self._proxy.plugin_name(index)
+        if plugin_name:
+            try:
+                return next(p for p in self.app.plugins_dir.plugins if p.name == plugin_name)
+            except StopIteration:
+                pass
+        return None
diff --git a/src/sakia/gui/dialogs/plugins_manager/plugins_manager.ui b/src/sakia/gui/dialogs/plugins_manager/plugins_manager.ui
new file mode 100644
index 0000000000000000000000000000000000000000..554fea28591736f959b60d027cf1d03cf934af12
--- /dev/null
+++ b/src/sakia/gui/dialogs/plugins_manager/plugins_manager.ui
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PluginDialog</class>
+ <widget class="QDialog" name="PluginDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>629</width>
+    <height>316</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plugins manager</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Installed plugins list</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QTableView" name="table_plugins">
+        <attribute name="horizontalHeaderStretchLastSection">
+         <bool>true</bool>
+        </attribute>
+        <attribute name="verticalHeaderStretchLastSection">
+         <bool>true</bool>
+        </attribute>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_5">
+        <property name="topMargin">
+         <number>6</number>
+        </property>
+        <item>
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="button_save">
+          <property name="text">
+           <string>Install a new plugin</string>
+          </property>
+          <property name="iconSize">
+           <size>
+            <width>16</width>
+            <height>16</height>
+           </size>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="button_delete">
+          <property name="text">
+           <string>Uninstall selected plugin</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="button_box">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../../../../../res/icons/icons.qrc"/>
+ </resources>
+ <connections/>
+ <slots>
+  <slot>open_manage_wallet_coins()</slot>
+  <slot>change_displayed_wallet(int)</slot>
+  <slot>transfer_mode_changed(bool)</slot>
+  <slot>recipient_mode_changed(bool)</slot>
+  <slot>change_current_community(int)</slot>
+  <slot>amount_changed()</slot>
+  <slot>relative_amount_changed()</slot>
+ </slots>
+</ui>
diff --git a/src/sakia/gui/dialogs/plugins_manager/table_model.py b/src/sakia/gui/dialogs/plugins_manager/table_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdf0ee1c1ebeaad801e25a8127e500f2ad9365f4
--- /dev/null
+++ b/src/sakia/gui/dialogs/plugins_manager/table_model.py
@@ -0,0 +1,130 @@
+from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, QSortFilterProxyModel,\
+    QModelIndex, QT_TRANSLATE_NOOP
+from sakia.data.entities import Plugin
+
+
+class PluginsFilterProxyModel(QSortFilterProxyModel):
+    def __init__(self, parent):
+        """
+        History of all transactions
+        :param PyQt5.QtWidgets.QWidget parent: parent widget
+        """
+        super().__init__(parent)
+        self.app = None
+
+    def columnCount(self, parent):
+        return self.sourceModel().columnCount(None) - 1
+
+    def setSourceModel(self, source_model):
+        self.app = source_model.app
+        super().setSourceModel(source_model)
+
+    def lessThan(self, left, right):
+        """
+        Sort table by given column number.
+        """
+        source_model = self.sourceModel()
+        left_data = source_model.data(left, Qt.DisplayRole)
+        right_data = source_model.data(right, Qt.DisplayRole)
+        return left_data < right_data
+
+    def plugin_name(self, index):
+        """
+        Gets available table data at given index
+        :param index:
+        :return: tuple containing (Identity, Transfer)
+        """
+        if index.isValid() and index.row() < self.rowCount(QModelIndex()):
+            source_index = self.mapToSource(index)
+            contact_name_col = PluginsTableModel.columns_types.index('name')
+            contact_name = self.sourceModel().contacts_data[source_index.row()][contact_name_col]
+            return contact_name
+        return None
+    
+    def data(self, index, role):
+        source_index = self.mapToSource(index)
+        model = self.sourceModel()
+        source_data = model.data(source_index, role)
+        return source_data
+
+
+class PluginsTableModel(QAbstractTableModel):
+    """
+    A Qt abstract item model to display plugins in a table view
+    """
+
+    columns_types = (
+        'name',
+        'description',
+        'version',
+        'imported'
+    )
+
+    columns_headers = (
+        QT_TRANSLATE_NOOP("PluginsTableModel", 'Name'),
+        QT_TRANSLATE_NOOP("PluginsTableModel", 'Description'),
+        QT_TRANSLATE_NOOP("PluginsTableModel", 'Version'),
+        QT_TRANSLATE_NOOP("PluginsTableModel", 'Imported'),
+    )
+
+    def __init__(self, parent, app):
+        """
+        History of all transactions
+        :param PyQt5.QtWidgets.QWidget parent: parent widget
+        :param sakia.app.Application app: the main application
+        """
+        super().__init__(parent)
+        self.app = app
+        self.plugins_data = []
+
+    def add_plugin(self, plugin):
+        self.beginInsertRows(QModelIndex(), len(self.plugins_data), len(self.plugins_data))
+        self.plugins_data.append(self.data_plugin(plugin))
+        self.endInsertRows()
+
+    def remove_plugin(self, plugin):
+        for i, data in enumerate(self.plugins_data):
+            if data[PluginsTableModel.columns_types.index('name')] == plugin.name:
+                self.beginRemoveRows(QModelIndex(), i, i)
+                self.plugins_data.pop(i)
+                self.endRemoveRows()
+                return
+
+    def data_plugin(self, plugin):
+        """
+        Converts a plugin to table data
+        :param sakia.data.entities.Plugin plugin: the plugin
+        :return: data as tuple
+        """
+        return plugin.name, plugin.description, plugin.version, plugin.imported
+
+    def init_plugins(self):
+        self.beginResetModel()
+        self.plugins_data = []
+        for plugin in self.app.plugins_dir.plugins:
+            self.plugins_data.append(self.data_plugin(plugin))
+        self.endResetModel()
+
+    def rowCount(self, parent):
+        return len(self.plugins_data)
+
+    def columnCount(self, parent):
+        return len(PluginsTableModel.columns_types)
+
+    def headerData(self, section, orientation, role):
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            return PluginsTableModel.columns_headers[section]
+
+    def data(self, index, role):
+        row = index.row()
+        col = index.column()
+
+        if not index.isValid():
+            return QVariant()
+
+        if role in (Qt.DisplayRole, Qt.ForegroundRole, Qt.ToolTipRole):
+            return self.plugins_data[row][col]
+
+    def flags(self, index):
+        return Qt.ItemIsSelectable | Qt.ItemIsEnabled
+
diff --git a/src/sakia/gui/dialogs/plugins_manager/view.py b/src/sakia/gui/dialogs/plugins_manager/view.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2530b6868daaefb48b41530e05ad8eaa14ec46b
--- /dev/null
+++ b/src/sakia/gui/dialogs/plugins_manager/view.py
@@ -0,0 +1,36 @@
+from PyQt5.QtWidgets import QDialog, QAbstractItemView, QHeaderView
+from PyQt5.QtCore import QModelIndex
+from .plugins_manager_uic import Ui_PluginDialog
+
+
+class PluginsManagerView(QDialog, Ui_PluginDialog):
+    """
+    The view of the plugins manager component
+    """
+
+    def __init__(self, parent):
+        """
+
+        :param parent:
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+    def set_table_plugins_model(self, model):
+        """
+        Define the table history model
+        :param QAbstractItemModel model:
+        :return:
+        """
+        self.table_plugins.setModel(model)
+        self.table_plugins.setSelectionBehavior(QAbstractItemView.SelectRows)
+        self.table_plugins.setSortingEnabled(True)
+        self.table_plugins.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
+        self.table_plugins.resizeRowsToContents()
+        self.table_plugins.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
+
+    def selected_plugin_index(self):
+        indexes = self.table_plugins.selectedIndexes()
+        if indexes:
+            return indexes[0]
+        return QModelIndex()
diff --git a/src/sakia/gui/main_window/controller.py b/src/sakia/gui/main_window/controller.py
index 2681c3f83c7cdd0060f93534589ff19a06c4b41c..b53ab1704e89bfdc9f471d03842b20b95c4df2f2 100644
--- a/src/sakia/gui/main_window/controller.py
+++ b/src/sakia/gui/main_window/controller.py
@@ -24,9 +24,9 @@ class MainWindowController(QObject):
         Init
         :param MainWindowView view: the ui of the mainwindow component
         :param sakia.gui.main_window.model.MainWindowModel: the model of the mainwindow component
-        :param sakia.gui.status_bar.controller.StatusBarController status_bar: the controller of the status bar component
-        :param sakia.gui.toolbar.controller.ToolbarController toolbar: the controller of the toolbar component
-        :param sakia.gui.navigation.contoller.NavigationController navigation: the controller of the navigation
+        :param sakia.gui.main_window.status_bar.controller.StatusBarController status_bar: the controller of the status bar component
+        :param sakia.gui.main_window.toolbar.controller.ToolbarController toolbar: the controller of the toolbar component
+        :param sakia.gui.navigation.controller.NavigationController navigation: the controller of the navigation
 
         :param PasswordAsker password_asker: the password asker of the application
         :type: sakia.core.app.Application
@@ -83,6 +83,7 @@ class MainWindowController(QObject):
             main_window.view.showMaximized()
         else:
             main_window.view.show()
+        main_window.model.load_plugins(main_window)
         main_window.refresh(app.currency)
         return main_window
 
diff --git a/src/sakia/gui/main_window/model.py b/src/sakia/gui/main_window/model.py
index 81714afea5e593e32bdb1aea37d63e4d47cef4f4..fa6f19a799bc5768961aeaf90280163e4cf36710 100644
--- a/src/sakia/gui/main_window/model.py
+++ b/src/sakia/gui/main_window/model.py
@@ -10,3 +10,7 @@ class MainWindowModel(QObject):
         super().__init__(parent)
         self.app = app
 
+    def load_plugins(self, main_window):
+        for plugin in self.app.plugins_dir.plugins:
+            if plugin.imported:
+                plugin.module.plugin_exec(self.app, main_window)
diff --git a/src/sakia/gui/main_window/toolbar/controller.py b/src/sakia/gui/main_window/toolbar/controller.py
index 94ae960c37eb4874cec53deec1ce99c3e5e1c155..5cb8a8f6b38661284851c1660a84fcedd0849347 100644
--- a/src/sakia/gui/main_window/toolbar/controller.py
+++ b/src/sakia/gui/main_window/toolbar/controller.py
@@ -5,9 +5,11 @@ from sakia.gui.dialogs.connection_cfg.controller import ConnectionConfigControll
 from sakia.gui.dialogs.revocation.controller import RevocationController
 from sakia.gui.dialogs.transfer.controller import TransferController
 from sakia.gui.dialogs.contact.controller import ContactController
+from sakia.gui.dialogs.plugins_manager.controller import PluginsManagerController
 from sakia.gui.preferences import PreferencesDialog
 from .model import ToolbarModel
 from .view import ToolbarView
+import sys
 
 
 class ToolbarController(QObject):
@@ -28,6 +30,7 @@ class ToolbarController(QObject):
         self.view.button_send_money.clicked.connect(self.open_transfer_money_dialog)
         self.view.action_add_connection.triggered.connect(self.open_add_connection_dialog)
         self.view.action_parameters.triggered.connect(self.open_settings_dialog)
+        self.view.action_plugins.triggered.connect(self.open_plugins_manager_dialog)
         self.view.action_about.triggered.connect(self.open_about_dialog)
         self.view.action_revoke_uid.triggered.connect(self.open_revocation_dialog)
         self.view.button_contacts.clicked.connect(self.open_contacts_dialog)
@@ -66,6 +69,9 @@ class ToolbarController(QObject):
     def open_settings_dialog(self):
         PreferencesDialog(self.model.app).exec()
 
+    def open_plugins_manager_dialog(self):
+        PluginsManagerController.open_dialog(self, self.model.app)
+
     def open_add_connection_dialog(self):
         connection_config = ConnectionConfigController.create_connection(self, self.model.app)
         connection_config.exec()
diff --git a/src/sakia/gui/main_window/toolbar/view.py b/src/sakia/gui/main_window/toolbar/view.py
index 11d406ca07bf36c242cffda03c0ddfd24a7b277c..df886770090fa6fdc02232e79a1790560b570365 100644
--- a/src/sakia/gui/main_window/toolbar/view.py
+++ b/src/sakia/gui/main_window/toolbar/view.py
@@ -27,6 +27,9 @@ class ToolbarView(QFrame, Ui_SakiaToolbar):
         self.action_parameters = QAction(self.tr("Settings"), tool_menu)
         tool_menu.addAction(self.action_parameters)
 
+        self.action_plugins = QAction(self.tr("Plugins manager"), tool_menu)
+        tool_menu.addAction(self.action_plugins)
+
         self.action_about = QAction(self.tr("About"), tool_menu)
         tool_menu.addAction(self.action_about)