diff --git a/src/jamidht/accountarchive.cpp b/src/jamidht/accountarchive.cpp
index 83082f1b14b4086d7842c4a75e4b46475d775b6a..0c23719a463c48872c9670294818a790436fe589 100644
--- a/src/jamidht/accountarchive.cpp
+++ b/src/jamidht/accountarchive.cpp
@@ -78,6 +78,10 @@ AccountArchive::deserialize(const std::vector<uint8_t>& dat)
     } catch (const std::exception& ex) {
         JAMI_ERR("Can't parse JSON: %s", ex.what());
     }
+
+    if (not id.first) {
+        throw std::runtime_error("Archive doesn't include account private key");
+    }
 }
 
 std::string
diff --git a/src/jamidht/archive_account_manager.cpp b/src/jamidht/archive_account_manager.cpp
index de90098f202ae38e2a43bee342e5e4b5db42c139..1418e9b6107d1c66d0034078d1d99dede1a0e932 100644
--- a/src/jamidht/archive_account_manager.cpp
+++ b/src/jamidht/archive_account_manager.cpp
@@ -68,10 +68,24 @@ ArchiveAccountManager::initAuthentication(
             auto& this_ = *static_cast<ArchiveAccountManager*>(&accountManager);
             try {
                 if (ctx->credentials->scheme == "file") {
-                    this_.loadFromFile(ctx);
-                    return;
+                    // Import from external archive
+                    this_.loadFromFile(*ctx);
                 } else {
-                    if (ctx->credentials->updateIdentity.first and ctx->credentials->updateIdentity.second) {
+                    // Create/migrate local account
+                    bool hasArchive = not ctx->credentials->uri.empty()
+                        and fileutils::isFile(ctx->credentials->uri);
+                    if (hasArchive) {
+                        // Create/migrate from local archive
+                        if (ctx->credentials->updateIdentity.first and
+                            ctx->credentials->updateIdentity.second and
+                            needsMigration(ctx->credentials->updateIdentity))
+                        {
+                            this_.migrateAccount(*ctx);
+                        } else {
+                            this_.loadFromFile(*ctx);
+                        }
+                    }
+                    else if (ctx->credentials->updateIdentity.first and ctx->credentials->updateIdentity.second) {
                         auto future_keypair = dht::ThreadPool::computation().get<dev::KeyPair>(&dev::KeyPair::create);
                         AccountArchive a;
                         JAMI_WARN("[Auth] converting certificate from old account %s", ctx->credentials->updateIdentity.first->getPublicKey().getId().toString().c_str());
@@ -80,11 +94,10 @@ ArchiveAccountManager::initAuthentication(
                             a.ca_key = std::make_shared<dht::crypto::PrivateKey>(fileutils::loadFile("ca.key", this_.path_));
                         } catch (...) {}
                         this_.updateCertificates(a, ctx->credentials->updateIdentity);
-                        auto keypair = future_keypair.get();
-                        a.eth_key = keypair.secret().makeInsecure().asBytes();
+                        a.eth_key = future_keypair.get().secret().makeInsecure().asBytes();
                         this_.onArchiveLoaded(*ctx, std::move(a));
                     } else {
-                        this_.createAccount(ctx);
+                        this_.createAccount(*ctx);
                     }
                 }
             } catch (const std::exception& e) {
@@ -137,7 +150,7 @@ ArchiveAccountManager::updateCertificates(AccountArchive& archive, dht::crypto::
 }
 
 void
-ArchiveAccountManager::createAccount(const std::shared_ptr<AuthContext>& ctx)
+ArchiveAccountManager::createAccount(AuthContext& ctx)
 {
     AccountArchive a;
     auto future_keypair = dht::ThreadPool::computation().get<dev::KeyPair>(&dev::KeyPair::create);
@@ -155,21 +168,22 @@ ArchiveAccountManager::createAccount(const std::shared_ptr<AuthContext>& ctx)
     a.ca_key = ca.first;
     auto keypair = future_keypair.get();
     a.eth_key = keypair.secret().makeInsecure().asBytes();
-    onArchiveLoaded(*ctx, std::move(a));
+    onArchiveLoaded(ctx, std::move(a));
 }
 
 void
-ArchiveAccountManager::loadFromFile(const std::shared_ptr<AuthContext>& ctx)
+ArchiveAccountManager::loadFromFile(AuthContext& ctx)
 {
+    JAMI_WARN("[Auth] loading archive from: %s", ctx.credentials->uri.c_str());
     AccountArchive archive;
     try {
-        archive = AccountArchive(ctx->credentials->uri, ctx->credentials->password);
+        archive = AccountArchive(ctx.credentials->uri, ctx.credentials->password);
     } catch (const std::exception& ex) {
         JAMI_WARN("[Auth] can't read file: %s", ex.what());
-        ctx->onFailure(AuthError::UNKNOWN, ex.what());
+        ctx.onFailure(AuthError::INVALID_ARGUMENTS, ex.what());
         return;
     }
-    onArchiveLoaded(*ctx, std::move(archive));
+    onArchiveLoaded(ctx, std::move(archive));
 }
 
 struct ArchiveAccountManager::DhtLoadContext {
@@ -259,6 +273,25 @@ ArchiveAccountManager::loadFromDHT(const std::shared_ptr<AuthContext>& ctx)
     dht::ThreadPool::computation().run(std::bind(search, false));
 }
 
+void
+ArchiveAccountManager::migrateAccount(AuthContext& ctx)
+{
+    JAMI_WARN("[Auth] account migration needed");
+    AccountArchive archive;
+    try {
+        archive = readArchive(ctx.credentials->password);
+    } catch (...) {
+        JAMI_DBG("[Auth] Can't load archive");
+        ctx.onFailure(AuthError::INVALID_ARGUMENTS, "");
+        return;
+    }
+
+    if (updateCertificates(archive, ctx.credentials->updateIdentity)) {
+        onArchiveLoaded(ctx, std::move(archive));
+    } else
+        ctx.onFailure(AuthError::UNKNOWN, "");
+}
+
 void
 ArchiveAccountManager::onArchiveLoaded(
     AuthContext& ctx,
@@ -273,7 +306,7 @@ ArchiveAccountManager::onArchiveLoaded(
     if (not a.id.second->isCA()) {
         JAMI_ERR("[Auth] trying to sign a certificate with a non-CA.");
     }
-    JAMI_WARN("generating device certificate");
+    JAMI_WARN("[Auth] creating new device certificate");
 
     auto request = ctx.request.get();
     if (not request->verify()) {
diff --git a/src/jamidht/archive_account_manager.h b/src/jamidht/archive_account_manager.h
index c6c103f4a3ab46ed6d94f394e6308e09d67a8368..5a63aba8dec5d2655a9bf42a8e56e32cef6c750c 100644
--- a/src/jamidht/archive_account_manager.h
+++ b/src/jamidht/archive_account_manager.h
@@ -37,7 +37,6 @@ public:
         {};
 
     struct ArchiveAccountCredentials : AccountCredentials {
-        std::string archivePath;
         in_port_t dhtPort;
         std::vector<std::string> dhtBootstrap;
         dht::crypto::Identity updateIdentity;
@@ -73,14 +72,15 @@ private:
     struct DhtLoadContext;
     struct AuthContext {
         CertRequest request;
-        //std::unique_ptr<dht::crypto::CertificateRequest> request;
         std::unique_ptr<ArchiveAccountCredentials> credentials;
         std::unique_ptr<DhtLoadContext> dhtContext;
         AuthSuccessCallback onSuccess;
         AuthFailureCallback onFailure;
     };
 
-    void createAccount(const std::shared_ptr<AuthContext>& ctx);
+    void createAccount(AuthContext& ctx);
+    void migrateAccount(AuthContext& ctx);
+
     std::pair<std::string, std::shared_ptr<dht::Value>> makeReceipt(const dht::crypto::Identity& id, const dht::crypto::Certificate& device, const std::string& ethAccount);
     void updateArchive(AccountArchive& content/*, const ContactList& syncData*/) const;
     void saveArchive(AccountArchive& content, const std::string& pwd);
@@ -89,7 +89,7 @@ private:
     bool updateCertificates(AccountArchive& archive, dht::crypto::Identity& device);
     static bool needsMigration(const dht::crypto::Identity& id);
 
-    void loadFromFile(const std::shared_ptr<AuthContext>& ctx);
+    void loadFromFile(AuthContext& ctx);
     void loadFromDHT(const std::shared_ptr<AuthContext>& ctx);
     void onArchiveLoaded(AuthContext& ctx, AccountArchive&& a);
 
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index aaffbf2c669ffceb971ed2e81e5cd6d8a5a268ef..2a41c7afa4a5a83692ff8267bd3ed8045a2dd861 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -904,9 +904,6 @@ JamiAccount::loadAccount(const std::string& archive_password, const std::string&
         }
 
         auto id = accountManager_->loadIdentity(tlsCertificateFile_, tlsPrivateKeyFile_, tlsPassword_);
-        /*bool hasArchive = not archivePath_.empty()
-            and fileutils::isFile(fileutils::getFullPath(idPath_, archivePath_));*/
-
         if (auto info = accountManager_->useIdentity(id, receipt_, receiptSignature_, std::move(callbacks))) {
             // normal loading path
             id_ = std::move(id);
@@ -917,6 +914,13 @@ JamiAccount::loadAccount(const std::string& archive_password, const std::string&
             }
         }
         else if (isEnabled()) {
+            if (not managerUri_.empty() and archive_password.empty()) {
+                Migration::setState(accountID_, Migration::State::INVALID);
+                setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION);
+                return;
+            }
+
+            bool migrating = registrationState_ == RegistrationState::ERROR_NEED_MIGRATION;
             setRegistrationState(RegistrationState::INITIALIZING);
             auto fDeviceKey = dht::ThreadPool::computation().getShared<std::shared_ptr<dht::crypto::PrivateKey>>([](){
                 return std::make_shared<dht::crypto::PrivateKey>(dht::crypto::PrivateKey::generate());
@@ -937,16 +941,24 @@ JamiAccount::loadAccount(const std::string& archive_password, const std::string&
                 if (archivePath_.empty()) {
                     archivePath_ = "archive.gz";
                 }
-                acreds->archivePath = archivePath_;
+                auto archivePath = fileutils::getFullPath(idPath_, archivePath_);
+                bool hasArchive = fileutils::isFile(archivePath);
                 if (not archive_path.empty()) {
+                    // Importing external archive
                     acreds->scheme = "file";
                     acreds->uri = archive_path;
                 }
                 else if (not archive_pin.empty()) {
+                    // Importing from DHT
                     acreds->scheme = "dht";
                     acreds->uri = archive_pin;
                     acreds->dhtBootstrap = loadBootstrap();
                     acreds->dhtPort = (in_port_t)dhtPortUsed_;
+                } else if (hasArchive) {
+                    // Migrating local account
+                    acreds->scheme = "local";
+                    acreds->uri = std::move(archivePath);
+                    acreds->updateIdentity = id;
                 }
                 creds = std::move(acreds);
             } else {
@@ -960,12 +972,12 @@ JamiAccount::loadAccount(const std::string& archive_password, const std::string&
             accountManager_->initAuthentication(
                 std::move(fReq),
                 std::move(creds),
-                [this, fDeviceKey](const AccountInfo& info,
+                [this, fDeviceKey, migrating](const AccountInfo& info,
                     const std::map<std::string, std::string>& config,
                     std::string&& receipt,
                     std::vector<uint8_t>&& receipt_signature)
             {
-                JAMI_WARN("Auth success !");
+                JAMI_WARN("[Account %s] Auth success !", getAccountID().c_str());
 
                 fileutils::check_dir(idPath_.c_str(), 0700);
 
@@ -994,16 +1006,25 @@ JamiAccount::loadAccount(const std::string& archive_password, const std::string&
                 receipt_ = std::move(receipt);
                 receiptSignature_ = std::move(receipt_signature);
                 accountManager_->foundAccountDevice(info.identity.second, ringDeviceName_, clock::now());
+                if (migrating) {
+                    Migration::setState(getAccountID(), Migration::State::SUCCESS);
+                }
                 setRegistrationState(RegistrationState::UNREGISTERED);
                 saveConfig();
                 doRegister();
-            }, [this](AccountManager::AuthError error, const std::string& message)
-            {
-                JAMI_WARN("Auth error: %d %s", (int)error, message.c_str());
-                setRegistrationState(RegistrationState::ERROR_GENERIC);
-                runOnMainThread([id = getAccountID()] {
-                    Manager::instance().removeAccount(id, true);
-                });
+            }, [w = weak(), id = getAccountID()](AccountManager::AuthError error, const std::string& message) {
+                JAMI_WARN("[Account %s] Auth error: %d %s", id.c_str(), (int)error, message.c_str());
+                if (error == AccountManager::AuthError::INVALID_ARGUMENTS) {
+                    Migration::setState(id, Migration::State::INVALID);
+                    if (auto acc = w.lock())
+                        acc->setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION);
+                } else {
+                    if (auto acc = w.lock())
+                        acc->setRegistrationState(RegistrationState::ERROR_GENERIC);
+                    runOnMainThread([id = std::move(id)] {
+                        Manager::instance().removeAccount(id, true);
+                    });
+                }
             }, std::move(callbacks));
         }
     }