diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index e75f37502eeb5a3f24ec6da3dafa91482da9c6cb..cc23613dbf891c569e1bac7b3add3d1cf31fe1a4 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -173,6 +173,10 @@ public:
     std::pair<bool, bool> dirtyConversations {true, true}; ///< true if filteredConversations/customFilteredConversations must be regenerated
     std::map<std::string, std::mutex> interactionsLocks; ///< {convId, mutex}
 
+    // Synchronization tools for account creation synchronization
+    std::mutex m_mutex_conversations;
+    std::condition_variable m_condVar_conversation_creation;
+
 public Q_SLOTS:
     /**
      * Listen from contactModel when updated (like new alias, avatar, etc.)
@@ -560,11 +564,13 @@ ConversationModel::placeCall(const std::string& uid)
 void
 ConversationModel::sendMessage(const std::string& uid, const std::string& body)
 {
+    // FIXME potential race condition between index check and at() call
     auto conversationIdx = pimpl_->indexOf(uid);
     if (conversationIdx == -1)
         return;
 
     auto& conversation = pimpl_->conversations.at(conversationIdx);
+
     if (conversation.participants.empty()) {
         // Should not
         qDebug() << "ConversationModel::sendMessage can't send a interaction to a conversation with no participant";
@@ -575,11 +581,16 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body)
     auto accountId = pimpl_->accountProfileId;
     bool isTemporary = conversation.participants.front() == "";
 
-    // Send interaction to all participants
-    // NOTE: conferences are not implemented yet, so we have only one participant
     uint64_t daemonMsgId = 0;
     auto status = interaction::Status::SENDING;
-    for (const auto& participant: conversation.participants) {
+
+    /* Make a copy of participants list: if current conversation is temporary,
+       it might me destroyed while we are reading it */
+    const auto participants = conversation.participants;
+
+    /* Check participants list, send contact request if needed.
+       NOTE: conferences are not implemented yet, so we have only one participant */
+    for (const auto& participant: participants) {
         auto contactInfo = owner.contactModel->getContact(participant);
 
         if (contactInfo.isBanned) {
@@ -588,25 +599,27 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body)
         }
 
         pimpl_->sendContactRequest(participant);
+    }
 
-        QStringList callLists = CallManager::instance().getCallList(); // no auto
-        // workaround: sometimes, it may happen that the daemon delete a call, but lrc don't. We check if the call is
-        //             still valid every time the user want to send a message.
-        if (not conversation.callId.empty() and not callLists.contains(conversation.callId.c_str()))
-            conversation.callId.clear();
-
-        if (not conversation.callId.empty()
-            and call::canSendSIPMessage(owner.callModel->getCall(conversation.callId))) {
-            status = interaction::Status::UNKNOWN;
-            owner.callModel->sendSipMessage(conversation.callId, body);
-
-        } else
-            daemonMsgId = owner.contactModel->sendDhtMessage(contactInfo.profileInfo.uri, body);
+    if (isTemporary) {
+        /* Block until we are sure that the final conversation was created by
+           slotContactAdded(). If adding contact failed we should not process
+           any further */
+        std::unique_lock<std::mutex> lock(pimpl_->m_mutex_conversations);
+        auto res = pimpl_->m_condVar_conversation_creation.wait_for(lock, std::chrono::seconds(2), [&]() {
+            return pimpl_->indexOfContact(convId) >= 0;
+        });
+        lock.unlock();
 
+        if (!res) {
+            qDebug() << "ConversationModel::sendMessage reached timeout while waiting for contact to be added. Couldn't send message.";
+            return;
+        }
     }
 
-    // If first interaction with temporary contact, we have to update the conversations info
-    // at this stage
+    /* Now we should be able to retrieve the final conversation, in case the previous
+       one was temporary */
+    // FIXME potential race condition between index check and at() call
     int contactIndex;
     if (isTemporary && (contactIndex = pimpl_->indexOfContact(convId)) < 0) {
         qDebug() << "Can't send message: Other participant is not a contact";
@@ -616,19 +629,50 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body)
     auto& newConv = isTemporary ? pimpl_->conversations.at(contactIndex) : conversation;
     convId = newConv.uid;
 
+    // Send interaction to each participant
+    for (const auto& participant: newConv.participants) {
+        auto contactInfo = owner.contactModel->getContact(participant);
+
+        QStringList callLists = CallManager::instance().getCallList(); // no auto
+        // workaround: sometimes, it may happen that the daemon delete a call, but lrc don't. We check if the call is
+        //             still valid every time the user want to send a message.
+        if (not newConv.callId.empty() and not callLists.contains(newConv.callId.c_str()))
+            newConv.callId.clear();
+
+        if (not newConv.callId.empty()
+            and call::canSendSIPMessage(owner.callModel->getCall(newConv.callId))) {
+            status = interaction::Status::UNKNOWN;
+            owner.callModel->sendSipMessage(newConv.callId, body);
+
+        } else {
+            daemonMsgId = owner.contactModel->sendDhtMessage(contactInfo.profileInfo.uri, body);
+        }
+
+    }
+
     // Add interaction to database
     auto msg = interaction::Info {accountId, body, std::time(nullptr),
                                   interaction::Type::TEXT, status};
     int msgId = database::addMessageToConversation(pimpl_->db, accountId, convId, msg);
+
     // Update conversation
     if (status == interaction::Status::SENDING) {
         // Because the daemon already give an id for the message, we need to store it.
         database::addDaemonMsgId(pimpl_->db, std::to_string(msgId), std::to_string(daemonMsgId));
     }
+
+    bool ret = false;
+
     {
         std::lock_guard<std::mutex> lk(pimpl_->interactionsLocks[convId]);
-        newConv.interactions.insert(std::pair<uint64_t, interaction::Info>(msgId, msg));
+        ret = newConv.interactions.insert(std::pair<uint64_t, interaction::Info>(msgId, msg)).second;
     }
+
+    if (!ret) {
+        qDebug("ConversationModel::sendMessage failed to send message because an existing key was already present in the database (key = %d)", msgId);
+        return;
+    }
+
     newConv.lastMessageUid = msgId;
     pimpl_->dirtyConversations = {true, true};
     // Emit this signal for chatview in the client
@@ -1173,6 +1217,7 @@ ConversationModelPimpl::slotContactAdded(const std::string& uri)
     }
 
     sortConversations();
+    m_condVar_conversation_creation.notify_all();
     emit linked.modelSorted();
 }