diff --git a/.gitmodules b/.gitmodules
index e72cd9aa8c0ca3eb10eecfdfc32b083664677211..46aa7d0b5dc94c67edfc41d6a3f27bd2427527b2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -27,3 +27,7 @@
 	path = 3rdparty/tidy-html5
 	url = https://github.com/htacg/tidy-html5.git
 	ignore = dirty
+[submodule "3rdparty/zxing-cpp"]
+	path = 3rdparty/zxing-cpp
+	url = https://github.com/nu-book/zxing-cpp.git
+        ignore = dirty
diff --git a/3rdparty/zxing-cpp b/3rdparty/zxing-cpp
new file mode 160000
index 0000000000000000000000000000000000000000..a920817b6fe0508cc4aca9003003c2812a78e935
--- /dev/null
+++ b/3rdparty/zxing-cpp
@@ -0,0 +1 @@
+Subproject commit a920817b6fe0508cc4aca9003003c2812a78e935
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 46db8e9718385538db3a19d985a7f02b2c4aca2a..64eee5ffb9e250523bae5ff2cc27fda19c1a5f2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -364,6 +364,8 @@ set(COMMON_SOURCES
   ${APP_SRC_DIR}/pluginversionmanager.cpp
   ${APP_SRC_DIR}/connectioninfolistmodel.cpp
   ${APP_SRC_DIR}/pluginversionmanager.cpp
+  ${APP_SRC_DIR}/linkdevicemodel.cpp
+  ${APP_SRC_DIR}/qrcodescannermodel.cpp
 )
 
 set(COMMON_HEADERS
@@ -436,6 +438,8 @@ set(COMMON_HEADERS
   ${APP_SRC_DIR}/pttlistener.h
   ${APP_SRC_DIR}/crashreportclient.h
   ${APP_SRC_DIR}/crashreporter.h
+  ${APP_SRC_DIR}/linkdevicemodel.h
+  ${APP_SRC_DIR}/qrcodescannermodel.h
 )
 
 # For libavutil/avframe.
@@ -678,6 +682,15 @@ list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
 list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
 list(APPEND CLIENT_LIBS tidy-static)
 
+# ZXing-cpp configuration
+set(BUILD_EXAMPLES OFF CACHE BOOL "")
+set(BUILD_BLACKBOX_TESTS OFF CACHE BOOL "")
+add_subdirectory(3rdparty/zxing-cpp)
+
+# Add ZXing-cpp to includes and libraries
+list(APPEND CLIENT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/zxing-cpp/core/src)
+list(APPEND CLIENT_LIBS ZXing)
+
 # common executable sources
 qt_add_executable(
   ${PROJECT_NAME}
diff --git a/src/app/linkdevicemodel.cpp b/src/app/linkdevicemodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cc3f2c3b579bf9f06dc9ccd8650b17ec742b067
--- /dev/null
+++ b/src/app/linkdevicemodel.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025-2025 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "linkdevicemodel.h"
+#include "lrcinstance.h"
+#include "api/accountmodel.h"
+
+#include "api/account.h"
+
+using namespace lrc::api::account;
+
+LinkDeviceModel::LinkDeviceModel(LRCInstance* lrcInstance, QObject* parent)
+    : QObject(parent)
+    , lrcInstance_(lrcInstance)
+{
+    set_deviceAuthState(static_cast<int>(DeviceAuthState::INIT));
+    connect(&lrcInstance_->accountModel(),
+            &lrc::api::AccountModel::addDeviceStateChanged,
+            this,
+            [this](const QString& accountId,
+                   uint32_t operationId,
+                   int state,
+                   const MapStringString& details) {
+                if (operationId != operationId_)
+                    return;
+
+                auto deviceState = static_cast<DeviceAuthState>(state);
+
+                switch (deviceState) {
+                case DeviceAuthState::CONNECTING:
+                    handleConnectingSignal();
+                    break;
+                case DeviceAuthState::AUTHENTICATING:
+                    handleAuthenticatingSignal(Utils::mapStringStringToVariantMap(details));
+                    break;
+                case DeviceAuthState::IN_PROGRESS:
+                    handleInProgressSignal();
+                    break;
+                case DeviceAuthState::DONE:
+                    handleDoneSignal(Utils::mapStringStringToVariantMap(details));
+                    break;
+                default:
+                    break;
+                }
+            });
+}
+
+void
+LinkDeviceModel::addDevice(const QString& token)
+{
+    set_tokenErrorMessage("");
+    auto errorMessage = QObject::tr(
+        "New device identifier is not recognized.\nPlease follow above instruction.");
+
+    if (!token.startsWith("jami-auth://") || (token.length() != 59)) {
+        set_tokenErrorMessage(errorMessage);
+        return;
+    }
+
+    int32_t result = lrcInstance_->accountModel().addDevice(lrcInstance_->getCurrentAccountInfo().id,
+                                                            token);
+    if (result > 0) {
+        operationId_ = result;
+    } else {
+        set_tokenErrorMessage(errorMessage);
+    }
+}
+
+void
+LinkDeviceModel::handleConnectingSignal()
+{
+    set_deviceAuthState(static_cast<int>(DeviceAuthState::CONNECTING));
+}
+
+void
+LinkDeviceModel::handleAuthenticatingSignal(const QVariantMap& details)
+{
+    QString peerAddress = details.value("peer_address").toString();
+    set_ipAddress(peerAddress);
+    set_deviceAuthState(static_cast<int>(DeviceAuthState::AUTHENTICATING));
+}
+
+void
+LinkDeviceModel::handleInProgressSignal()
+{
+    set_deviceAuthState(static_cast<int>(DeviceAuthState::IN_PROGRESS));
+}
+
+void
+LinkDeviceModel::handleDoneSignal(const QVariantMap& details)
+{
+    QString errorString = details.value("error").toString();
+    if (!errorString.isEmpty() && errorString != "none") {
+        auto error = mapLinkDeviceError(errorString.toStdString());
+        set_linkDeviceError(getLinkDeviceString(error));
+        set_deviceAuthState(static_cast<int>(DeviceAuthState::DONE));
+    } else {
+        set_deviceAuthState(static_cast<int>(DeviceAuthState::DONE));
+    }
+}
+
+void
+LinkDeviceModel::confirmAddDevice()
+{
+    handleInProgressSignal();
+    lrcInstance_->accountModel().confirmAddDevice(lrcInstance_->getCurrentAccountInfo().id,
+                                                  operationId_);
+}
+
+void
+LinkDeviceModel::cancelAddDevice()
+{
+    handleInProgressSignal();
+    lrcInstance_->accountModel().cancelAddDevice(lrcInstance_->getCurrentAccountInfo().id,
+                                                 operationId_);
+}
+
+void
+LinkDeviceModel::reset()
+{
+    set_deviceAuthState(static_cast<int>(DeviceAuthState::INIT));
+
+    set_linkDeviceError("");
+    set_ipAddress("");
+    set_tokenErrorMessage("");
+}
diff --git a/src/app/linkdevicemodel.h b/src/app/linkdevicemodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf27d445d4c93462906114fc0e0bce898fc6302d
--- /dev/null
+++ b/src/app/linkdevicemodel.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2025-2025 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "api/account.h"
+
+#include "qmladapterbase.h"
+#include "qtutils.h"
+
+#include <QObject>
+#include <QVariant>
+#include <QMap>
+
+class LRCInstance;
+
+class LinkDeviceModel : public QObject
+{
+    Q_OBJECT
+    QML_PROPERTY(QString, tokenErrorMessage);
+    QML_PROPERTY(QString, linkDeviceError);
+    QML_PROPERTY(int, deviceAuthState);
+    QML_PROPERTY(QString, ipAddress);
+
+public:
+    explicit LinkDeviceModel(LRCInstance* lrcInstance, QObject* parent = nullptr);
+
+    Q_INVOKABLE void addDevice(const QString& token);
+
+    Q_INVOKABLE void confirmAddDevice();
+    Q_INVOKABLE void cancelAddDevice();
+    Q_INVOKABLE void reset();
+
+private:
+    bool checkNewStateValidity(lrc::api::account::DeviceAuthState newState) const;
+    void handleConnectingSignal();
+    void handleAuthenticatingSignal(const QVariantMap& details);
+    void handleInProgressSignal();
+    void handleDoneSignal(const QVariantMap& details);
+
+    LRCInstance* lrcInstance_ = nullptr;
+    uint32_t operationId_;
+};
diff --git a/src/app/net/jami/Constants/JamiStrings.qml b/src/app/net/jami/Constants/JamiStrings.qml
index d848e849d4a74e297a8947434178217904338017..328f1bb36a882bd908a7c76e3f98fba8928b291a 100644
--- a/src/app/net/jami/Constants/JamiStrings.qml
+++ b/src/app/net/jami/Constants/JamiStrings.qml
@@ -74,7 +74,7 @@ Item {
     property string scanToImportAccount: qsTr("Scan this QR code on your other device to proceed with importing your account.")
     property string waitingForToken: qsTr("Please wait…")
     property string scanQRCode: qsTr("Scan QR code")
-    property string connectingToDevice: qsTr("Action required.\nPlease confirm account on your old device.")
+    property string connectingToDevice: qsTr("Action required.\nPlease confirm account on the source device.")
     property string confirmAccountImport: qsTr("Authenticating device")
     property string transferringAccount: qsTr("Transferring account…")
     property string cantScanQRCode: qsTr("If you are unable to scan the QR code, enter this token on your other device to proceed.")
@@ -600,12 +600,17 @@ Item {
     property string enterAccountPassword: qsTr("Enter account password")
     property string enterPasswordPinCode: qsTr("This account is password encrypted, enter the password to generate a PIN code.")
     property string addDevice: qsTr("Add Device")
-    property string pinExpired: qsTr("PIN code has expired.")
-    property string onAnotherDevice: qsTr("On another device")
-    property string onAnotherDeviceInstruction: qsTr("Install and launch Jami, select “Import from another device” and scan the QR code.")
     property string linkNewDevice: qsTr("Link new device")
-    property string linkingInstructions: qsTr("In Jami, scan the QR code or manually enter the PIN code.")
-    property string pinValidity: qsTr("The PIN code will expire in: ")
+    property string linkDeviceConnecting: qsTr("Connecting to your new device…")
+    property string linkDeviceInProgress: qsTr("The export account operation to the new device is in progress.\nPlease confirm the import on the new device.")
+    property string linkDeviceScanQR: qsTr("On the new device, initiate a new account.\nSelect Add account -> Connect from another device.\nWhen ready, scan the QR code.")
+    property string linkDeviceEnterManually: qsTr("Alternatively you could enter the authentication code manually.")
+    property string linkDeviceEnterCodePlaceholder: qsTr("Enter authentication code")
+    property string linkDeviceAllSet: qsTr("You are all set!\nYour account is successfully imported on the new device!")
+    property string linkDeviceFoundAddress: qsTr("New device found at address below. Is that you?\nClicking on confirm will continue transfering account.")
+    property string linkDeviceNewDeviceIP: qsTr("New device IP address: %1")
+    property string linkDeviceCloseWarningTitle: qsTr("Do you want to exit?")
+    property string linkDeviceCloseWarningMessage: qsTr("Exiting will cancel the import account operation.")
 
     // PasswordDialog
     property string enterPassword: qsTr("Enter password")
diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp
index de94474ed619e03b61f2f7f96e7f58f1d3eaa831..eacff9e22c8e28ebd632fe4c9b0cedd9924a9a44 100644
--- a/src/app/qmlregister.cpp
+++ b/src/app/qmlregister.cpp
@@ -62,6 +62,8 @@
 #include "pluginlistpreferencemodel.h"
 #include "preferenceitemlistmodel.h"
 #include "wizardviewstepmodel.h"
+#include "linkdevicemodel.h"
+#include "qrcodescannermodel.h"
 
 #include "api/peerdiscoverymodel.h"
 #include "api/codecmodel.h"
@@ -185,6 +187,12 @@ registerTypes(QQmlEngine* engine,
     QQmlEngine::setObjectOwnership(wizardViewStepModel, QQmlEngine::CppOwnership);
     REG_QML_SINGLETON<WizardViewStepModel>(REG_MODEL, "WizardViewStepModel", CREATE(wizardViewStepModel));
 
+    // LinkDeviceModel
+    auto linkdevicemodel = new LinkDeviceModel(lrcInstance);
+    qApp->setProperty("LinkDeviceModel", QVariant::fromValue(linkdevicemodel));
+    QQmlEngine::setObjectOwnership(linkdevicemodel, QQmlEngine::CppOwnership);
+    REG_QML_SINGLETON<LinkDeviceModel>(REG_MODEL, "LinkDeviceModel", CREATE(linkdevicemodel));
+
     // Register app-level objects that are used by QML created objects.
     // These MUST be set prior to loading the initial QML file, in order to
     // be available to the QML adapter class factory creation methods.
@@ -195,6 +203,7 @@ registerTypes(QQmlEngine* engine,
     qApp->setProperty("PreviewEngine", QVariant::fromValue(previewEngine));
 
     // qml adapter registration
+    QML_REGISTERSINGLETON_TYPE(NS_HELPERS, QRCodeScannerModel);
     QML_REGISTERSINGLETON_TYPE(NS_HELPERS, AvatarRegistry);
     QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, AccountAdapter);
     QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CallAdapter);
diff --git a/src/app/qrcodescannermodel.cpp b/src/app/qrcodescannermodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b1159fb0d2e60c8ac8483f1da3693dbbd13a812c
--- /dev/null
+++ b/src/app/qrcodescannermodel.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025-2025 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qrcodescannermodel.h"
+
+#include <Barcode.h>
+#include <MultiFormatReader.h>
+#include <ReadBarcode.h>
+
+#include <QDebug>
+
+QRCodeScannerModel::QRCodeScannerModel(QObject* parent)
+    : QObject(parent)
+{}
+
+QString
+QRCodeScannerModel::scanImage(const QImage& image)
+{
+    if (image.isNull())
+        return QString();
+
+    // Convert QImage to grayscale and get raw data
+    QImage grayImage = image.convertToFormat(QImage::Format_Grayscale8);
+    int width = grayImage.width();
+    int height = grayImage.height();
+
+    try {
+        // Create ZXing image
+        ZXing::ImageView imageView(grayImage.bits(), width, height, ZXing::ImageFormat::Lum);
+
+        // Configure reader
+        ZXing::ReaderOptions options;
+        options.setTryHarder(true);
+        options.setTryRotate(true);
+        options.setFormats(ZXing::BarcodeFormat::QRCode);
+
+        // Try to detect QR code
+        auto result = ZXing::ReadBarcode(imageView, options);
+
+        if (result.isValid()) {
+            QString text = QString::fromStdString(result.text());
+            return text;
+        }
+    } catch (const std::exception& e) {
+        qWarning() << "QR code scanning error:" << e.what();
+    }
+
+    return QString();
+}
diff --git a/src/app/qrcodescannermodel.h b/src/app/qrcodescannermodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..39c95961e77e68e00720d083b393fb4e843e0004
--- /dev/null
+++ b/src/app/qrcodescannermodel.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025-2025 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+#include <QImage>
+
+#include <QQmlEngine> // QML registration
+
+class QRCodeScannerModel : public QObject
+{
+    Q_OBJECT
+
+public:
+    static QRCodeScannerModel* create(QQmlEngine*, QJSEngine*)
+    {
+        return new QRCodeScannerModel();
+    }
+
+    explicit QRCodeScannerModel(QObject* parent = nullptr);
+
+    Q_INVOKABLE QString scanImage(const QImage& image);
+};
diff --git a/src/app/settingsview/components/LinkDeviceDialog.qml b/src/app/settingsview/components/LinkDeviceDialog.qml
index 7148d502014273046889e23c97a0a32d1640d608..ca49e5c2684207fb6363e9046a68b43c9115fd6d 100644
--- a/src/app/settingsview/components/LinkDeviceDialog.qml
+++ b/src/app/settingsview/components/LinkDeviceDialog.qml
@@ -20,6 +20,8 @@ import QtQuick.Layouts
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
+import net.jami.Enums 1.1
+import Qt.labs.platform
 import "../../commoncomponents"
 import "../../mainview/components"
 
@@ -32,368 +34,381 @@ BaseModalDialog {
 
     property bool darkTheme: UtilsAdapter.useApplicationTheme()
 
-    popupContent: StackLayout {
-        id: stackedWidget
+    autoClose: false
+    closeButtonVisible: false
 
-        function setGeneratingPage() {
-            if (passwordEdit.length === 0 && CurrentAccount.hasArchivePassword) {
-                setExportPage(NameDirectory.ExportOnRingStatus.WRONG_PASSWORD, "");
-                return;
-            }
-            stackedWidget.currentIndex = exportingSpinnerPage.pageIndex;
-            spinnerMovie.playing = true;
-        }
-
-        function setExportPage(status, pin) {
-            if (status === NameDirectory.ExportOnRingStatus.SUCCESS) {
-                infoLabel.success = true;
-                pinRectangle.visible = true
-                exportedPIN.text = pin;
-            } else {
-                infoLabel.success = false;
-                infoLabel.visible = true;
-                switch (status) {
-                case NameDirectory.ExportOnRingStatus.WRONG_PASSWORD:
-                    infoLabel.text = JamiStrings.incorrectPassword;
-                    break;
-                case NameDirectory.ExportOnRingStatus.NETWORK_ERROR:
-                    infoLabel.text = JamiStrings.linkDeviceNetWorkError;
-                    break;
-                case NameDirectory.ExportOnRingStatus.INVALID:
-                    infoLabel.text = JamiStrings.somethingWentWrong;
-                    break;
-                }
-            }
-            stackedWidget.currentIndex = exportingInfoPage.pageIndex;
-            stackedWidget.height = exportingLayout.implicitHeight;
-        }
+    // Function to check if dialog can be closed directly
+    function canCloseDirectly() {
+        return LinkDeviceModel.deviceAuthState === DeviceAuthStateEnum.INIT ||
+               LinkDeviceModel.deviceAuthState === DeviceAuthStateEnum.DONE
+    }
 
-        onVisibleChanged: {
-            if (visible) {
-                if (CurrentAccount.hasArchivePassword) {
-                    stackedWidget.currentIndex = enterPasswordPage.pageIndex;
-                } else {
-                    setGeneratingPage();
-                }
-            }
+    // Close button. Use custom close button to show a confirmation dialog.
+    JamiPushButton {
+        anchors {
+            top: parent.top
+            right: parent.right
+            topMargin: 5
+            rightMargin: 5
         }
 
-        // Index = 0
-        Item {
-            id: enterPasswordPage
+        Layout.preferredHeight: 20
+        Layout.preferredWidth: 20
 
-            readonly property int pageIndex: 0
+        imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
+        normalColor: "transparent"
 
-            Component.onCompleted: passwordEdit.forceActiveFocus()
-
-            onHeightChanged: {
-                stackedWidget.height = passwordLayout.implicitHeight
+        source: JamiResources.round_close_24dp_svg
+        onClicked: {
+            if (canCloseDirectly()) {
+                root.close();
+            } else {
+                confirmCloseDialog.open();
             }
+        }
+    }
 
-            ColumnLayout {
-                id: passwordLayout
-                spacing: JamiTheme.preferredMarginSize
-                anchors.centerIn: parent
-
-                Label {
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.maximumWidth: root.width - 4 * JamiTheme.preferredMarginSize
-                    wrapMode: Text.Wrap
-
-                    text: JamiStrings.enterPasswordPinCode
-                    color: JamiTheme.textColor
-                    font.pointSize: JamiTheme.textFontSize
-                    font.kerning: true
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-                }
-
-                RowLayout {
-                    Layout.topMargin: 10
-                    Layout.leftMargin: JamiTheme.cornerIconSize
-                    Layout.rightMargin: JamiTheme.cornerIconSize
-                    spacing: JamiTheme.preferredMarginSize
-                    Layout.bottomMargin: JamiTheme.preferredMarginSize
-
-                    PasswordTextEdit {
-                        id: passwordEdit
-
-                        firstEntry: true
-                        placeholderText: JamiStrings.password
+    MessageDialog {
+        id: confirmCloseDialog
 
-                        Layout.alignment: Qt.AlignLeft
-                        Layout.fillWidth: true
+        text: JamiStrings.linkDeviceCloseWarningTitle
+        informativeText: JamiStrings.linkDeviceCloseWarningMessage
+        buttons: MessageDialog.Ok | MessageDialog.Cancel
 
-                        KeyNavigation.up: btnConfirm
-                        KeyNavigation.down: KeyNavigation.up
+        onOkClicked: function(button) {
+            root.close();
+        }
+    }
 
-                        onDynamicTextChanged: {
-                            btnConfirm.enabled = dynamicText.length > 0;
-                            btnConfirm.hoverEnabled = dynamicText.length > 0;
+    popupContent: Item {
+        id: content
+        width: 400
+        height: 450
+
+        // Scrollable container for StackLayout
+        ScrollView {
+            id: scrollView
+
+            anchors.fill: parent
+
+            anchors.leftMargin: 20
+            anchors.rightMargin: 20
+            anchors.bottomMargin: 20
+            clip: true
+
+            ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+            ScrollBar.vertical.policy: ScrollBar.AsNeeded
+            contentHeight: stackLayout.implicitHeight
+
+            StackLayout {
+                id: stackLayout
+                width: Math.min(scrollView.width, scrollView.availableWidth)
+
+                currentIndex: scanAndEnterCodeView.index
+
+                Connections {
+                    target: LinkDeviceModel
+
+                    function onDeviceAuthStateChanged() {
+                        switch (LinkDeviceModel.deviceAuthState) {
+                        case DeviceAuthStateEnum.INIT:
+                            stackLayout.currentIndex = scanAndEnterCodeView.index
+                            break
+                        case DeviceAuthStateEnum.CONNECTING:
+                            stackLayout.currentIndex = deviceLinkLoadingView.index
+                            deviceLinkLoadingView.loadingText = JamiStrings.linkDeviceConnecting
+                            break
+                        case DeviceAuthStateEnum.AUTHENTICATING:
+                            stackLayout.currentIndex = deviceConfirmationView.index
+                            break
+                        case DeviceAuthStateEnum.IN_PROGRESS:
+                            stackLayout.currentIndex = deviceLinkLoadingView.index
+                            deviceLinkLoadingView.loadingText = JamiStrings.linkDeviceInProgress
+                            break
+                        case DeviceAuthStateEnum.DONE:
+                            if (LinkDeviceModel.linkDeviceError.length > 0) {
+                                stackLayout.currentIndex = deviceLinkErrorView.index
+                            } else {
+                                stackLayout.currentIndex = deviceLinkSuccessView.index
+                            }
+                            break
+                        default:
+                            break
                         }
-                        onAccepted: btnConfirm.clicked()
                     }
+                }
 
-                    JamiPushButton {
-                        id: btnConfirm
-
-                        Layout.alignment: Qt.AlignCenter
-                        height: 36
-                        width: 36
-
-                        hoverEnabled: false
-                        enabled: false
-
-                        imageColor: JamiTheme.secondaryBackgroundColor
-                        hoveredColor: JamiTheme.buttonTintedBlueHovered
-                        source: JamiResources.check_black_24dp_svg
-                        normalColor: JamiTheme.tintedBlue
+                // Common base component for stack layout items
+                component StackViewBase: Item {
+                    id: baseItem
 
-                        onClicked: stackedWidget.setGeneratingPage()
+                    required property string title
+                    default property alias content: contentLayout.data
 
+                    Layout.fillWidth: true
+                    Layout.alignment: Qt.AlignHCenter
+                    implicitHeight: contentLayout.implicitHeight
+
+                    ColumnLayout {
+                        id: contentLayout
+                        anchors {
+                            left: parent.left
+                            right: parent.right
+                            verticalCenter: parent.verticalCenter
+                        }
+                        Layout.preferredWidth: scrollView.width
+                        spacing: 20
                     }
                 }
-            }
-        }
-
-        // Index = 1
-        Item {
-            id: exportingSpinnerPage
-
-            readonly property int pageIndex: 1
-
-            onHeightChanged: {
-                stackedWidget.height = spinnerLayout.implicitHeight
-            }
-            onWidthChanged: stackedWidget.width = exportingLayout.implicitWidth
 
-            ColumnLayout {
-                id: spinnerLayout
-
-                spacing: JamiTheme.preferredMarginSize
-                anchors.centerIn: parent
-
-                Label {
-                    Layout.alignment: Qt.AlignCenter
+                StackViewBase {
+                    id: deviceLinkErrorView
+                    property int index: 0
+                    title: "Error"
+
+                    Text {
+                        Layout.alignment: Qt.AlignHCenter
+                        text: LinkDeviceModel.linkDeviceError
+                        Layout.preferredWidth: scrollView.width
+                        color: JamiTheme.textColor
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                    }
 
-                    text: JamiStrings.linkDevice
-                    color: JamiTheme.textColor
-                    font.pointSize: JamiTheme.headerFontSize
-                    font.kerning: true
-                    horizontalAlignment: Text.AlignLeft
-                    verticalAlignment: Text.AlignVCenter
+                    MaterialButton {
+                        Layout.alignment: Qt.AlignHCenter
+                        text: JamiStrings.close
+                        toolTipText: JamiStrings.optionTryAgain
+                        primary: true
+                        onClicked: {
+                            root.close();
+                        }
+                    }
                 }
 
-                AnimatedImage {
-                    id: spinnerMovie
-
-                    Layout.alignment: Qt.AlignCenter
-
-                    Layout.preferredWidth: 30
-                    Layout.preferredHeight: 30
+                StackViewBase {
+                    id: deviceLinkSuccessView
+                    property int index: 1
+                    title: "Success"
+
+                    Text {
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        text: JamiStrings.linkDeviceAllSet
+                        color: JamiTheme.textColor
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                    }
 
-                    source: JamiResources.jami_rolling_spinner_gif
-                    playing: visible
-                    fillMode: Image.PreserveAspectFit
-                    mipmap: true
+                    MaterialButton {
+                        Layout.alignment: Qt.AlignHCenter
+                        text: JamiStrings.close
+                        toolTipText: JamiStrings.optionTryAgain
+                        primary: true
+                        onClicked: {
+                            root.close();
+                        }
+                    }
                 }
-            }
-        }
-
-        // Index = 2
-        Item {
-            id: exportingInfoPage
-
-            readonly property int pageIndex: 2
-
-            width: childrenRect.width
-            height: childrenRect.height
-
-            onHeightChanged: {
-                stackedWidget.height = exportingLayout.implicitHeight
-            }
-            onWidthChanged: stackedWidget.width = exportingLayout.implicitWidth
-
-            ColumnLayout {
-                id: exportingLayout
-
-                spacing: JamiTheme.preferredMarginSize
 
-                Label {
-                    id: instructionLabel
-
-                    Layout.maximumWidth: Math.min(root.maximumPopupWidth, root.width) - 2 * root.popupMargins
-                    Layout.alignment: Qt.AlignLeft
-
-                    color: JamiTheme.textColor
-
-                    wrapMode: Text.Wrap
-                    text: JamiStrings.linkingInstructions
-                    font.pointSize: JamiTheme.textFontSize
-                    font.kerning: true
-                    verticalAlignment: Text.AlignVCenter
-
-                }
+                StackViewBase {
+                    id: deviceLinkLoadingView
+                    property int index: 2
+                    title: "Loading"
+                    property string loadingText: ""
+
+                    BusyIndicator {
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: 50
+                        Layout.preferredHeight: 50
+                        running: true
+                    }
 
-                RowLayout {
-                    spacing: 10
-                    Layout.maximumWidth: Math.min(root.maximumPopupWidth, root.width) - 2 * root.popupMargins
-
-                    Rectangle {
-                        Layout.alignment: Qt.AlignCenter
-
-                        radius: 5
-                        color: JamiTheme.backgroundRectangleColor
-                        width: 100
-                        height: 100
-
-                        Rectangle {
-                            width: qrImage.width + 4
-                            height: qrImage.height + 4
-                            anchors.centerIn: parent
-                            radius: 5
-                            color: JamiTheme.whiteColor
-                            Image {
-                                 id: qrImage
-                                 anchors.centerIn: parent
-                                 mipmap: false
-                                 smooth: false
-                                 source: "image://qrImage/raw_" + exportedPIN.text
-                                 sourceSize.width: 80
-                                 sourceSize.height: 80
+                    Text {
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        text: deviceLinkLoadingView.loadingText
+                        color: JamiTheme.textColor
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                        opacity: 1
+
+                        SequentialAnimation on opacity {
+                            running: true
+                            loops: Animation.Infinite
+                            NumberAnimation {
+                                from: 1
+                                to: 0.3
+                                duration: 1000
+                                easing.type: Easing.InOutQuad
+                            }
+                            NumberAnimation {
+                                from: 0.3
+                                to: 1
+                                duration: 1000
+                                easing.type: Easing.InOutQuad
                             }
                         }
-
                     }
+                }
 
-                    Rectangle {
-                        id: pinRectangle
-
-                        radius: 5
-                        color: JamiTheme.backgroundRectangleColor
-                        Layout.fillWidth: true
-                        height: 100
-                        Layout.minimumWidth: exportedPIN.width + 20
-
-                        Layout.alignment: Qt.AlignCenter
-
-                        MaterialLineEdit {
-                            id: exportedPIN
-
-                            padding: 10
-                            anchors.centerIn: parent
-
-                            text: JamiStrings.pin
-                            wrapMode: Text.NoWrap
-
-                            backgroundColor: JamiTheme.backgroundRectangleColor
-
-                            color: darkTheme ? JamiTheme.editLineColor : JamiTheme.darkTintedBlue
-                            selectByMouse: true
-                            readOnly: true
-                            font.pointSize: JamiTheme.tinyCreditsTextSize
-                            font.kerning: true
-                            horizontalAlignment: Text.AlignHCenter
-                            verticalAlignment: Text.AlignVCenter
-                        }
+                StackViewBase {
+                    id: deviceConfirmationView
+                    property int index: 3
+                    title: "Confirmation"
+
+                    Text {
+                        id: explanationConnect
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        text: JamiStrings.linkDeviceFoundAddress
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                        color: JamiTheme.textColor
                     }
-                }
 
-                Rectangle {
-                    radius: 5
-                    color: JamiTheme.infoRectangleColor
-                    Layout.fillWidth: true
-                    Layout.preferredHeight: infoLabels.height + 38
+                    Text {
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        text: JamiStrings.linkDeviceNewDeviceIP.arg(LinkDeviceModel.ipAddress)
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                        color: JamiTheme.textColor
+                        font.weight: Font.Bold
+                    }
 
                     RowLayout {
-                        id: infoLayout
-
-                        anchors.centerIn: parent
-                        anchors.fill: parent
-                        anchors.margins: 14
-                        spacing: 10
-
-                        ResponsiveImage{
-                            Layout.fillWidth: true
-
-                            source: JamiResources.outline_info_24dp_svg
-                            fillMode: Image.PreserveAspectFit
-
-                            color: darkTheme ? JamiTheme.editLineColor : JamiTheme.darkTintedBlue
-                            Layout.fillHeight: true
-                        }
-
-                        ColumnLayout{
-                            id: infoLabels
-
-                            Layout.fillHeight: true
-                            Layout.fillWidth: true
-
-                            Label {
-                                id: otherDeviceLabel
-
-                                Layout.alignment: Qt.AlignLeft
-                                color: JamiTheme.textColor
-                                text: JamiStrings.onAnotherDevice
-
-                                font.pointSize: JamiTheme.smallFontSize
-                                font.kerning: true
-                                font.bold: true
+                        Layout.alignment: Qt.AlignHCenter
+                        spacing: 16
+
+                        MaterialButton {
+                            id: confirm
+                            primary: true
+                            Layout.alignment: Qt.AlignCenter
+                            text: JamiStrings.optionConfirm
+                            toolTipText: JamiStrings.optionConfirm
+                            onClicked: {
+                                LinkDeviceModel.confirmAddDevice()
                             }
+                        }
 
-                            Label {
-                                id: otherInstructionLabel
-
-                                Layout.fillWidth: true
-                                Layout.alignment: Qt.AlignLeft
-
-                                wrapMode: Text.Wrap
-                                color: JamiTheme.textColor
-                                text: JamiStrings.onAnotherDeviceInstruction
-
-                                font.pointSize: JamiTheme.smallFontSize
-                                font.kerning: true
+                        MaterialButton {
+                            id: cancel
+                            Layout.alignment: Qt.AlignCenter
+                            secondary: true
+                            toolTipText: JamiStrings.cancel
+                            textLeftPadding: JamiTheme.buttontextWizzardPadding / 2
+                            textRightPadding: JamiTheme.buttontextWizzardPadding / 2
+                            text: JamiStrings.cancel
+                            onClicked: {
+                                LinkDeviceModel.cancelAddDevice()
                             }
                         }
                     }
                 }
 
-                // Displays error messages
-                Label {
-                    id: infoLabel
+                StackViewBase {
+                    id: scanAndEnterCodeView
+                    property int index: 4
+                    title: "Scan"
+
+                    Component.onDestruction: {
+                        if (qrScanner) {
+                            qrScanner.stopScanner()
+                        }
+                    }
 
-                    visible: false
+                    Text {
+                        id: explanationScan
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        text: JamiStrings.linkDeviceScanQR
+                        font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                        lineHeight: JamiTheme.wizardViewTextLineHeight
+                        horizontalAlignment: Text.AlignHCenter
+                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                        color: JamiTheme.textColor
+                    }
 
-                    property bool success: false
-                    property int borderWidth: success ? 1 : 0
-                    property int borderRadius: success ? 15 : 0
-                    property string backgroundColor: success ? "whitesmoke" : "transparent"
-                    property string borderColor: success ? "lightgray" : "transparent"
+                    QRCodeScanner {
+                        id: qrScanner
+                        Layout.alignment: Qt.AlignHCenter
+                        width: 250
+                        height: width * aspectRatio
+                        visible: VideoDevices.listSize !== 0
 
-                    Layout.maximumWidth: JamiTheme.preferredDialogWidth
-                    Layout.margins: JamiTheme.preferredMarginSize
+                        onQrCodeDetected: function(code) {
+                            console.log("QR code detected:", code)
+                            LinkDeviceModel.addDevice(code)
+                        }
+                    }
 
-                    Layout.alignment: Qt.AlignCenter
+                    ColumnLayout {
+                        id: manualEntry
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: scrollView.width
+                        spacing: 10
 
-                    color: success ? JamiTheme.successLabelColor : JamiTheme.redColor
-                    padding: success ? 8 : 0
+                        Text {
+                            id: explanation
+                            Layout.alignment: Qt.AlignHCenter
+                            Layout.preferredWidth: scrollView.width
+                            text: JamiStrings.linkDeviceEnterManually
+                            font.pixelSize: JamiTheme.wizardViewDescriptionFontPixelSize
+                            lineHeight: JamiTheme.wizardViewTextLineHeight
+                            horizontalAlignment: Text.AlignHCenter
+                            wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                            color: JamiTheme.textColor
+                        }
 
-                    wrapMode: Text.Wrap
-                    font.pointSize: success ? JamiTheme.textFontSize : JamiTheme.textFontSize + 3
-                    font.kerning: true
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
+                        ModalTextEdit {
+                            id: codeInput
+                            Layout.alignment: Qt.AlignHCenter
+                            Layout.preferredWidth: scrollView.width
+                            Layout.preferredHeight: JamiTheme.preferredFieldHeight
+                            placeholderText: JamiStrings.linkDeviceEnterCodePlaceholder
+                        }
 
-                    background: Rectangle {
-                        id: infoLabelBackground
+                        Text {
+                            Layout.alignment: Qt.AlignHCenter
+                            Layout.maximumWidth: parent.width - 40
+                            visible: LinkDeviceModel.tokenErrorMessage.length > 0
+                            text: LinkDeviceModel.tokenErrorMessage
+                            font.pointSize: JamiTheme.tinyFontSize
+                            color: JamiTheme.redColor
+                            horizontalAlignment: Text.AlignHCenter
+                            wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+                        }
+                    }
 
-                        border.width: infoLabel.borderWidth
-                        border.color: infoLabel.borderColor
-                        radius: infoLabel.borderRadius
-                        color: JamiTheme.secondaryBackgroundColor
+                    MaterialButton {
+                        id: connect
+                        Layout.alignment: Qt.AlignHCenter
+                        primary: true
+                        text: JamiStrings.connect
+                        toolTipText: JamiStrings.connect
+                        enabled: codeInput.dynamicText.length > 0
+                        onClicked: {
+                            LinkDeviceModel.addDevice(codeInput.text)
+                        }
                     }
                 }
             }
         }
     }
+
+    //Reset everything when dialog is closed
+    onClosed: {
+        LinkDeviceModel.reset()
+    }
 }
diff --git a/src/app/settingsview/components/QRCodeScanner.qml b/src/app/settingsview/components/QRCodeScanner.qml
new file mode 100644
index 0000000000000000000000000000000000000000..1b15196359b5865541244d4c8bc71cfb8d8c2a13
--- /dev/null
+++ b/src/app/settingsview/components/QRCodeScanner.qml
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025-2025 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick
+import net.jami.Constants 1.1
+import net.jami.Adapters 1.1
+import net.jami.Helpers 1.1
+import "../../commoncomponents"
+
+Item {
+    id: root
+
+    property bool isScanning: false
+    property real aspectRatio: 0.5625
+
+    onVisibleChanged: {
+        if (visible) {
+            startScanner()
+        } else {
+            stopScanner()
+        }
+    }
+
+    Component.onDestruction: {
+        stopScanner()
+    }
+
+    Rectangle {
+        id: cameraContainer
+        anchors.horizontalCenter: parent.horizontalCenter
+        width: parent.width
+        height: parent.height
+        color: JamiTheme.primaryForegroundColor
+        clip: true
+
+        LocalVideo {
+            id: previewWidget
+            anchors.fill: parent
+            flip: true
+
+            // Camera not available
+            underlayItems: Text {
+                id: noCameraText
+                anchors.centerIn: parent
+                font.pointSize: 18
+                font.capitalization: Font.AllUppercase
+                color: "white"
+                text: JamiStrings.noCamera
+                visible: false  // Start hidden
+
+                // Delay "No Camera" message to avoid flashing it when camera is starting up.
+                // If camera starts successfully within 5 seconds, user won't see this message.
+                // If there's a camera issue, message will be shown after the delay.
+                Timer {
+                    id: visibilityTimer
+                    interval: 5000
+                    running: true
+                    repeat: false
+                    onTriggered: {
+                        noCameraText.visible = true
+                        destroy()  // Remove the timer after it's done
+                    }
+                }
+            }
+        }
+
+        // Scanning line animation
+        Rectangle {
+            id: scanLine
+            width: parent.width
+            height: 2
+            color: JamiTheme.whiteColor
+            opacity: 0.8
+            visible: root.isScanning && previewWidget.isRendering
+
+            SequentialAnimation on y {
+                running: root.isScanning
+                loops: Animation.Infinite
+                NumberAnimation {
+                    from: 0
+                    to: cameraContainer.height
+                    duration: 2500
+                    easing.type: Easing.InOutQuad
+                }
+                NumberAnimation {
+                    from: cameraContainer.height
+                    to: 0
+                    duration: 2500
+                    easing.type: Easing.InOutQuad
+                }
+            }
+        }
+    }
+
+    Timer {
+        id: scanTimer
+        interval: 500
+        repeat: true
+        running: root.isScanning && previewWidget.isRendering
+        onTriggered: {
+            var result = QRCodeScannerModel.scanImage(videoProvider.captureRawVideoFrame(VideoDevices.getDefaultDevice()));
+            if (result !== "") {
+                root.isScanning = false
+                root.qrCodeDetected(result)
+            }
+        }
+    }
+
+    signal qrCodeDetected(string code)
+
+    function startScanner() {
+        previewWidget.startWithId(VideoDevices.getDefaultDevice())
+        root.isScanning = true
+    }
+
+    function stopScanner() {
+        previewWidget.startWithId("")
+        root.isScanning = true
+    }
+}
diff --git a/src/app/wizardview/components/ImportFromDevicePage.qml b/src/app/wizardview/components/ImportFromDevicePage.qml
index b308ddf47337beb04ee261a427976bfe904dc4b9..3ef8fe46444b4d2934b0504369980fc446b19f0f 100644
--- a/src/app/wizardview/components/ImportFromDevicePage.qml
+++ b/src/app/wizardview/components/ImportFromDevicePage.qml
@@ -17,11 +17,11 @@
 import QtQuick
 import QtQuick.Layouts
 import QtQuick.Controls
-import QtQuick.Dialogs
 import net.jami.Adapters 1.1
 import net.jami.Models 1.1
 import net.jami.Constants 1.1
 import net.jami.Enums 1.1
+import Qt.labs.platform
 import "../../commoncomponents"
 import "../../mainview/components"
 
@@ -77,11 +77,9 @@ Rectangle {
         informativeText: JamiStrings.linkDeviceCloseWarningMessage
         buttons: MessageDialog.Ok | MessageDialog.Cancel
 
-        onButtonClicked: function(button) {
-            if (button === MessageDialog.Ok) {
-                AccountAdapter.cancelImportAccount();
-                WizardViewStepModel.previousStep();
-            }
+        onOkClicked: function(button) {
+            AccountAdapter.cancelImportAccount();
+            WizardViewStepModel.previousStep();
         }
     }
 
diff --git a/src/libclient/accountmodel.cpp b/src/libclient/accountmodel.cpp
index 3d0fdd511dbaa7f4ab90f92d7893d2ce8af474c9..a059a095f56b9ab84632bcf4329e86c631fff414 100644
--- a/src/libclient/accountmodel.cpp
+++ b/src/libclient/accountmodel.cpp
@@ -126,6 +126,18 @@ public Q_SLOTS:
                                     int state,
                                     const MapStringString& details);
 
+    /**
+     * Emit addDeviceStateChanged.
+     * @param accountId
+     * @param operationId
+     * @param state
+     * @param details
+     */
+    void slotAddDeviceStateChanged(const QString& accountID,
+                                   uint32_t operationId,
+                                   int state,
+                                   const MapStringString& details);
+
     /**
      * @param accountId
      * @param details
@@ -430,6 +442,10 @@ AccountModelPimpl::AccountModelPimpl(AccountModel& linked,
             &CallbacksHandler::deviceAuthStateChanged,
             &linked,
             &AccountModel::deviceAuthStateChanged);
+    connect(&callbacksHandler,
+            &CallbacksHandler::addDeviceStateChanged,
+            &linked,
+            &AccountModel::addDeviceStateChanged);
     connect(&callbacksHandler,
             &CallbacksHandler::nameRegistrationEnded,
             this,
@@ -627,6 +643,15 @@ AccountModelPimpl::slotDeviceAuthStateChanged(const QString& accountId,
     Q_EMIT linked.deviceAuthStateChanged(accountId, state, details);
 }
 
+void
+AccountModelPimpl::slotAddDeviceStateChanged(const QString& accountId,
+                                             uint32_t operationId,
+                                             int state,
+                                             const MapStringString& details)
+{
+    Q_EMIT linked.addDeviceStateChanged(accountId, operationId, state, details);
+}
+
 void
 AccountModelPimpl::slotNameRegistrationEnded(const QString& accountId,
                                              int status,
diff --git a/src/libclient/callbackshandler.cpp b/src/libclient/callbackshandler.cpp
index 308e234e01cb3f7d11779441d785522701d0a91c..d9f670f2687e1315a79998580c3fba5632bd2374 100644
--- a/src/libclient/callbackshandler.cpp
+++ b/src/libclient/callbackshandler.cpp
@@ -247,6 +247,12 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
             &CallbacksHandler::slotDeviceAuthStateChanged,
             Qt::QueuedConnection);
 
+    connect(&ConfigurationManager::instance(),
+            &ConfigurationManagerInterface::addDeviceStateChanged,
+            this,
+            &CallbacksHandler::slotAddDeviceStateChanged,
+            Qt::QueuedConnection);
+
     connect(&ConfigurationManager::instance(),
             &ConfigurationManagerInterface::nameRegistrationEnded,
             this,
@@ -687,6 +693,15 @@ CallbacksHandler::slotDeviceAuthStateChanged(const QString& accountId,
     Q_EMIT deviceAuthStateChanged(accountId, state, details);
 }
 
+void
+CallbacksHandler::slotAddDeviceStateChanged(const QString& accountId,
+                                            uint32_t operationId,
+                                            int state,
+                                            const MapStringString& details)
+{
+    Q_EMIT addDeviceStateChanged(accountId, operationId, state, details);
+}
+
 void
 CallbacksHandler::slotNameRegistrationEnded(const QString& accountId,
                                             int status,
diff --git a/src/libclient/callbackshandler.h b/src/libclient/callbackshandler.h
index 428cf54ce6e20199c4ccac7ead6dd845922eb25d..136980929f0c1cb2862dc6d632f336e975f00af8 100644
--- a/src/libclient/callbackshandler.h
+++ b/src/libclient/callbackshandler.h
@@ -244,6 +244,19 @@ Q_SIGNALS:
      */
     void deviceAuthStateChanged(const QString& accountId, int state, const MapStringString& details);
 
+    /**
+     * Add device state has changed
+     * @param accountId
+     * @param operationId
+     * @param state
+     * @param details map
+     */
+
+    void addDeviceStateChanged(const QString& accountId,
+                               uint32_t operationId,
+                               int state,
+                               const MapStringString& details);
+
     /**
      * Name registration has ended
      * @param accountId
@@ -587,6 +600,18 @@ private Q_SLOTS:
                                     int state,
                                     const MapStringString& details);
 
+    /**
+     * Add device state has changed
+     * @param accountId
+     * @param operationId
+     * @param state
+     * @param details map
+     */
+    void slotAddDeviceStateChanged(const QString& accountId,
+                                   uint32_t operationId,
+                                   int state,
+                                   const MapStringString& details);
+
     /**
      * Emit nameRegistrationEnded
      * @param accountId