diff --git a/src/libclient/api/callmodel.h b/src/libclient/api/callmodel.h
index 4c99b9c73ab57c3fb8dfc7f17a4898ef2ab89dcb..5ac5920211733bb9b23c53b548bc10f9c68334ed 100644
--- a/src/libclient/api/callmodel.h
+++ b/src/libclient/api/callmodel.h
@@ -446,14 +446,16 @@ Q_SIGNALS:
      */
     void callEnded(const QString& callId) const;
     /**
-     * Emitted when a call is incoming
+     * Emitted when a new call is available
+     * @param peerId the peer uri
      * @param callId
-     * @param fromId the peer uri
      * @param displayname
+     * @param isOutgoing
      */
-    void newIncomingCall(const QString& fromId,
-                         const QString& callId,
-                         const QString& displayname) const;
+    void newCall(const QString& peerId,
+                 const QString& callId,
+                 const QString& displayname,
+                 bool isOutgoing) const;
     /**
      * Emitted when a call is added to a conference
      * @param callId
diff --git a/src/libclient/api/contactmodel.h b/src/libclient/api/contactmodel.h
index 5820d36fb72cb37a7b8d8a3b044f2d256cc1eb3b..9cae1fdcade260fb88608fffe97b0f71b95481f8 100644
--- a/src/libclient/api/contactmodel.h
+++ b/src/libclient/api/contactmodel.h
@@ -154,9 +154,10 @@ Q_SIGNALS:
     /**
      * Connect this signal to know when a call is incoming.
      * @param fromId peer profile uri
-     * @param callId incoming call id
+     * @param callId call id
+     * @param isOutgoing
      */
-    void incomingCall(const QString& from, const QString& callId) const;
+    void newCall(const QString& from, const QString& callId, bool isOutgoing) const;
     /**
      * Connect this signal to know when a text message arrives for this account
      * @param accountId
diff --git a/src/libclient/api/conversationmodel.h b/src/libclient/api/conversationmodel.h
index 1e353934ef6585e4a83d2e83f7ac90b4700e95eb..07536a6feda92818da9637ff6f757cca869fdc94 100644
--- a/src/libclient/api/conversationmodel.h
+++ b/src/libclient/api/conversationmodel.h
@@ -241,6 +241,7 @@ public:
      * @param filter the new filter
      */
     void setFilter(const QString& filter);
+    void setFilterString(const QString& filter);
     /**
      * Modify the current filter (will change the result of getFilteredConversations)
      * @param filter the new filter (example: SIP,  RING,  REQUEST)
diff --git a/src/libclient/callbackshandler.cpp b/src/libclient/callbackshandler.cpp
index 549ca36d93199239b0049eecda7dfa52e9cb09be..254b361fa84bf68deac96fea1f935880c86e465b 100644
--- a/src/libclient/callbackshandler.cpp
+++ b/src/libclient/callbackshandler.cpp
@@ -153,18 +153,6 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent)
             &CallbacksHandler::slotRegistrationStateChanged,
             Qt::QueuedConnection);
 
-    connect(&CallManager::instance(),
-            &CallManagerInterface::incomingCall,
-            this,
-            &CallbacksHandler::slotIncomingCall,
-            Qt::QueuedConnection);
-
-    connect(&CallManager::instance(),
-            &CallManagerInterface::incomingCallWithMedia,
-            this,
-            &CallbacksHandler::slotIncomingCallWithMedia,
-            Qt::QueuedConnection);
-
     connect(&CallManager::instance(),
             &CallManagerInterface::mediaChangeRequested,
             this,
@@ -448,38 +436,6 @@ CallbacksHandler::slotContactRemoved(const QString& accountId,
     Q_EMIT contactRemoved(accountId, contactUri, banned);
 }
 
-void
-CallbacksHandler::slotIncomingCall(const QString& accountId,
-                                   const QString& callId,
-                                   const QString& fromUri)
-{
-    slotIncomingCallWithMedia(accountId, callId, fromUri, {});
-}
-
-void
-CallbacksHandler::slotIncomingCallWithMedia(const QString& accountId,
-                                            const QString& callId,
-                                            const QString& fromUri,
-                                            const VectorMapStringString& mediaList)
-{
-    QString displayname;
-    QString fromQString;
-    if (fromUri.contains("ring.dht")) {
-        auto qDisplayname = fromUri.left(fromUri.indexOf("<") + 1);
-        if (qDisplayname.size() > 2) {
-            displayname = qDisplayname.left(qDisplayname.indexOf("<") - 1);
-        }
-        fromQString = fromUri.right(50);
-        fromQString = fromQString.left(40);
-    } else {
-        auto left = fromUri.indexOf("<") + 1;
-        auto right = fromUri.indexOf("@");
-        fromQString = fromUri.mid(left, right - left);
-        displayname = fromUri.left(fromUri.indexOf("<") - 1);
-    }
-    Q_EMIT incomingCallWithMedia(accountId, callId, fromQString, displayname, mediaList);
-}
-
 void
 CallbacksHandler::slotMediaChangeRequested(const QString& accountId,
                                            const QString& callId,
diff --git a/src/libclient/callbackshandler.h b/src/libclient/callbackshandler.h
index d8e2699ff60e7a70d229b7dcdb25fae9b831cc63..4d62fdca079c2f6d0a65e377f22380c8d3443ce6 100644
--- a/src/libclient/callbackshandler.h
+++ b/src/libclient/callbackshandler.h
@@ -90,19 +90,6 @@ Q_SIGNALS:
      * @param confirmed if the contact is trusted.
      */
     void contactAdded(const QString& accountId, const QString& contactUri, bool confirmed);
-    /**
-     * Connect this signal to know when a call arrives
-     * @param accountId the one who receives the call
-     * @param callId the call id
-     * @param fromUri the caller uri
-     * @param displayName the display name of incoming call
-     * @param mediaList media received
-     */
-    void incomingCallWithMedia(const QString& accountId,
-                               const QString& callId,
-                               const QString& fromUri,
-                               const QString& displayName,
-                               const VectorMapStringString& mediaList);
     /**
      * Connect this signal to know when a call arrives
      * @param accountId the one who receives the call
@@ -452,25 +439,6 @@ private Q_SLOTS:
                                       const QString& registration_state,
                                       unsigned detail_code,
                                       const QString& detail_str);
-    /**
-     * @deprecated Use slotIncomingCallWithMedia instead
-     * Get the URI of the peer and emit incomingCall
-     * @param accountId account linked
-     * @param callId the incoming call id
-     * @param fromUri the uri of the peer
-     */
-    void slotIncomingCall(const QString& accountId, const QString& callId, const QString& fromUri);
-    /**
-     * Get the URI of the peer and emit incomingCallWithMedia
-     * @param accountId account linked
-     * @param callId the incoming call id
-     * @param fromUri the uri of the peer
-     * @param mediaList the mediaList received
-     */
-    void slotIncomingCallWithMedia(const QString& accountId,
-                                   const QString& callId,
-                                   const QString& fromUri,
-                                   const VectorMapStringString& mediaList);
     /**
      * Get the URI of the peer and emit mediaChangeRequested
      * @param accountId account linked
diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp
index 449184f0ed21a08a7f04ca5f45f31b062ce53e04..300366b429fb6180bc3109822ac275438cd32879 100644
--- a/src/libclient/callmodel.cpp
+++ b/src/libclient/callmodel.cpp
@@ -188,19 +188,6 @@ public:
     QList<call::PendingConferenceeInfo> pendingConferencees_;
 
 public Q_SLOTS:
-    /**
-     * Listen from CallbacksHandler when a call is incoming
-     * @param accountId account which receives the call
-     * @param callId
-     * @param fromId peer uri
-     * @param displayname
-     * @param mediaList media received
-     */
-    void slotIncomingCallWithMedia(const QString& accountId,
-                                   const QString& callId,
-                                   const QString& fromId,
-                                   const QString& displayname,
-                                   const VectorMapStringString& mediaList);
     /**
      * Connect this signal to know when a call arrives
      * @param accountId the one who receives the call
@@ -986,10 +973,6 @@ CallModelPimpl::CallModelPimpl(const CallModel& linked,
     , callbacksHandler(callbacksHandler)
     , behaviorController(behaviorController)
 {
-    connect(&callbacksHandler,
-            &CallbacksHandler::incomingCallWithMedia,
-            this,
-            &CallModelPimpl::slotIncomingCallWithMedia);
     connect(&callbacksHandler,
             &CallbacksHandler::mediaChangeRequested,
             this,
@@ -1367,56 +1350,6 @@ CallModel::isConferenceHost(const QString& callId)
         return call->second->type == lrc::api::call::Type::CONFERENCE;
 }
 
-void
-CallModelPimpl::slotIncomingCallWithMedia(const QString& accountId,
-                                          const QString& callId,
-                                          const QString& fromId,
-                                          const QString& displayname,
-                                          const VectorMapStringString& mediaList)
-{
-    if (linked.owner.id != accountId) {
-        return;
-    }
-    // TODO: uncomment this. For now, the rendez-vous account is showing calls
-    // if (linked.owner.confProperties.isRendezVous) {
-    //    // Do not notify for calls if rendez vous because it's in a detached
-    //    // mode and auto answer is managed by the daemon
-    //    return;
-    //}
-
-    auto callInfo = std::make_shared<call::Info>();
-    callInfo->id = callId;
-    // peer uri = ring:<jami_id> or sip number
-    auto uri = (linked.owner.profileInfo.type != profile::Type::SIP && !fromId.contains("ring:"))
-                   ? "ring:" + fromId
-                   : fromId;
-    callInfo->peerUri = uri;
-    callInfo->isOutgoing = false;
-    callInfo->status = call::Status::INCOMING_RINGING;
-    callInfo->type = call::Type::DIALOG;
-    callInfo->isAudioOnly = true;
-    callInfo->videoMuted = true;
-    for (const auto& item : mediaList) {
-        if (item[MediaAttributeKey::MEDIA_TYPE] == MediaAttributeValue::VIDEO) {
-            callInfo->isAudioOnly = false;
-            if (!checkMediaDeviceMuted(item)) {
-                callInfo->videoMuted = false;
-                break;
-            }
-        }
-    }
-    callInfo->mediaList = mediaList;
-    calls.emplace(callId, std::move(callInfo));
-
-    if (!linked.owner.confProperties.allowIncoming
-        && linked.owner.profileInfo.type == profile::Type::JAMI) {
-        linked.refuse(callId);
-        return;
-    }
-
-    Q_EMIT linked.newIncomingCall(fromId, callId, displayname);
-}
-
 void
 CallModelPimpl::slotMediaChangeRequested(const QString& accountId,
                                          const QString& callId,
@@ -1461,9 +1394,58 @@ CallModelPimpl::slotCallStateChanged(const QString& accountId,
                                      const QString& state,
                                      int code)
 {
-    if (accountId != linked.owner.id || !linked.hasCall(callId))
+    if (accountId != linked.owner.id)
         return;
 
+    if (!linked.hasCall(callId)) {
+        auto callInfo = std::make_shared<call::Info>();
+        callInfo->id = callId;
+        MapStringString details = CallManager::instance().getCallDetails(linked.owner.id, callId);
+        qDebug() << details;
+
+        auto endId = details["PEER_NUMBER"].indexOf("@");
+        callInfo->peerUri = details["PEER_NUMBER"].left(endId);
+        callInfo->isOutgoing = details["CALL_TYPE"] == "1";
+        callInfo->status = call::to_status(state);
+        callInfo->type = call::Type::DIALOG;
+        callInfo->isAudioOnly = details["AUDIO_ONLY"] == TRUE_STR;
+        callInfo->videoMuted = details["VIDEO_MUTED"] == TRUE_STR;
+        callInfo->mediaList = {};
+        calls.emplace(callId, std::move(callInfo));
+
+        if (!(details["CALL_TYPE"] == "1")
+            && !linked.owner.confProperties.allowIncoming
+            && linked.owner.profileInfo.type == profile::Type::JAMI) {
+            linked.refuse(callId);
+            return;
+        }
+
+        QString displayname = details["DISPLAY_NAME"];
+        QString peerId;
+        QString peerUri = details["PEER_NUMBER"];
+        if (peerUri.contains("ring.dht")) {
+            peerId = peerUri.right(50);
+            peerId = peerId.left(40);
+            if (displayname.isEmpty())
+                displayname = details["REGISTERED_NAME"];
+        } else {
+            auto left = std::max(peerUri.indexOf("<"), peerUri.indexOf(":")) + 1;
+            auto right = peerUri.indexOf("@");
+            right = std::max(right, peerUri.indexOf(">"));
+            peerId = peerUri.mid(left, right - left);
+            if (displayname.isEmpty())
+                displayname = peerId;
+        }
+        qDebug() << displayname;
+        qDebug() << peerId;
+
+        Q_EMIT linked.newCall(peerId, callId, displayname, details["CALL_TYPE"] == "1");
+
+        // NOTE: signal emission order matters, always emit CallStatusChanged before CallEnded
+        Q_EMIT linked.callStatusChanged(callId, code);
+        Q_EMIT behaviorController.callStatusChanged(linked.owner.id, callId);
+    }
+
     auto status = call::to_status(state);
     auto& call = calls[callId];
     if (!call)
diff --git a/src/libclient/contactmodel.cpp b/src/libclient/contactmodel.cpp
index c48e083592b46616f4806a56caa21b3bfb6c8d55..035fb07ebe92e6c01ab022f8ec8a506ed334bfe2 100644
--- a/src/libclient/contactmodel.cpp
+++ b/src/libclient/contactmodel.cpp
@@ -159,12 +159,13 @@ public Q_SLOTS:
                                  const QString& registeredName);
 
     /**
-     * Listen from callModel when an incoming call arrives.
+     * Listen from callModel when an new call is available.
      * @param fromId
      * @param callId
      * @param displayName
+     * @param isOutgoing
      */
-    void slotIncomingCall(const QString& fromId, const QString& callId, const QString& displayname);
+    void slotNewCall(const QString& fromId, const QString& callId, const QString& displayname, bool isOutgoing);
 
     /**
      * Listen from callbacksHandler for new account interaction and add pending contact if not present
@@ -413,6 +414,7 @@ ContactModel::getSearchResults() const
 void
 ContactModel::searchContact(const QString& query)
 {
+    qDebug() << "query! " << query;
     // always reset temporary contact
     pimpl_->searchResult.clear();
 
@@ -626,9 +628,9 @@ ContactModelPimpl::ContactModelPimpl(const ContactModel& linked,
             this,
             &ContactModelPimpl::slotRegisteredNameFound);
     connect(&*linked.owner.callModel,
-            &CallModel::newIncomingCall,
+            &CallModel::newCall,
             this,
-            &ContactModelPimpl::slotIncomingCall);
+            &ContactModelPimpl::slotNewCall);
     connect(&callbacksHandler,
             &lrc::CallbacksHandler::newAccountMessage,
             this,
@@ -666,9 +668,9 @@ ContactModelPimpl::~ContactModelPimpl()
                this,
                &ContactModelPimpl::slotRegisteredNameFound);
     disconnect(&*linked.owner.callModel,
-               &CallModel::newIncomingCall,
+               &CallModel::newCall,
                this,
-               &ContactModelPimpl::slotIncomingCall);
+               &ContactModelPimpl::slotNewCall);
     disconnect(&callbacksHandler,
                &lrc::CallbacksHandler::newAccountMessage,
                this,
@@ -1027,39 +1029,41 @@ ContactModelPimpl::slotRegisteredNameFound(const QString& accountId,
 }
 
 void
-ContactModelPimpl::slotIncomingCall(const QString& fromId,
-                                    const QString& callId,
-                                    const QString& displayname)
+ContactModelPimpl::slotNewCall(const QString& fromId,
+                               const QString& callId,
+                               const QString& displayname,
+                               bool isOutgoing)
 {
-    bool emitContactAdded = false;
-    {
-        std::lock_guard<std::mutex> lk(contactsMtx_);
-        auto it = contacts.find(fromId);
-        if (it == contacts.end()) {
-            // Contact not found, load profile from database.
-            // The conversation model will create an entry and link the incomingCall.
-            auto type = (linked.owner.profileInfo.type == profile::Type::JAMI)
-                            ? profile::Type::PENDING
-                            : profile::Type::SIP;
-            addToContacts(fromId, type, displayname, false);
-            emitContactAdded = true;
-        } else {
-            // Update the display name
-            if (!displayname.isEmpty()) {
-                it->profileInfo.alias = displayname;
-                storage::createOrUpdateProfile(linked.owner.id, it->profileInfo, true);
+    if (!isOutgoing) {
+        bool emitContactAdded = false;
+        {
+            std::lock_guard<std::mutex> lk(contactsMtx_);
+            auto it = contacts.find(fromId);
+            if (it == contacts.end()) {
+                // Contact not found, load profile from database.
+                // The conversation model will create an entry and link the incomingCall.
+                auto type = (linked.owner.profileInfo.type == profile::Type::JAMI)
+                                ? profile::Type::PENDING
+                                : profile::Type::SIP;
+                addToContacts(fromId, type, displayname, false);
+                emitContactAdded = true;
+            } else {
+                // Update the display name
+                if (!displayname.isEmpty()) {
+                    it->profileInfo.alias = displayname;
+                    storage::createOrUpdateProfile(linked.owner.id, it->profileInfo, true);
+                }
             }
         }
+        if (emitContactAdded) {
+            if (linked.owner.profileInfo.type == profile::Type::SIP)
+                Q_EMIT linked.contactAdded(fromId);
+            else if (linked.owner.profileInfo.type == profile::Type::JAMI)
+                Q_EMIT behaviorController.newTrustRequest(linked.owner.id, "", fromId);
+        } else
+            Q_EMIT linked.profileUpdated(fromId);
     }
-    if (emitContactAdded) {
-        if (linked.owner.profileInfo.type == profile::Type::SIP)
-            Q_EMIT linked.contactAdded(fromId);
-        else if (linked.owner.profileInfo.type == profile::Type::JAMI)
-            Q_EMIT behaviorController.newTrustRequest(linked.owner.id, "", fromId);
-    } else
-        Q_EMIT linked.profileUpdated(fromId);
-
-    Q_EMIT linked.incomingCall(fromId, callId);
+    Q_EMIT linked.newCall(fromId, callId, isOutgoing);
 }
 
 void
diff --git a/src/libclient/conversationmodel.cpp b/src/libclient/conversationmodel.cpp
index fa0331bd9c1ca8100d85e8b3957dac58e44447aa..5d853fb7984955dcb162324311efc3db7cb51f59 100644
--- a/src/libclient/conversationmodel.cpp
+++ b/src/libclient/conversationmodel.cpp
@@ -270,8 +270,9 @@ public Q_SLOTS:
      * Listen from callmodel for new calls.
      * @param fromId caller uri
      * @param callId
+     * @param isOutgoing
      */
-    void slotIncomingCall(const QString& fromId, const QString& callId);
+    void slotNewCall(const QString& fromId, const QString& callId, bool isOutgoing = false);
     /**
      * Listen from callmodel for calls status changed.
      * @param callId
@@ -1817,9 +1818,9 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
 
     // Call related
     connect(&*linked.owner.contactModel,
-            &ContactModel::incomingCall,
+            &ContactModel::newCall,
             this,
-            &ConversationModelPimpl::slotIncomingCall);
+            &ConversationModelPimpl::slotNewCall);
     connect(&*linked.owner.callModel,
             &lrc::api::CallModel::callStatusChanged,
             this,
@@ -1972,9 +1973,9 @@ ConversationModelPimpl::~ConversationModelPimpl()
 
     // Call related
     disconnect(&*linked.owner.contactModel,
-               &ContactModel::incomingCall,
+               &ContactModel::newCall,
                this,
-               &ConversationModelPimpl::slotIncomingCall);
+               &ConversationModelPimpl::slotNewCall);
     disconnect(&*linked.owner.callModel,
                &lrc::api::CallModel::callStatusChanged,
                this,
@@ -3366,24 +3367,37 @@ ConversationModelPimpl::getIndicesForContact(const QString& uri) const
 }
 
 void
-ConversationModelPimpl::slotIncomingCall(const QString& fromId, const QString& callId)
+ConversationModelPimpl::slotNewCall(const QString& fromId, const QString& callId, bool isOutgoing)
 {
+    if (isOutgoing) {
+        // search contact
+        currentFilter = fromId;
+        invalidateModel();
+        searchResults.clear();
+        Q_EMIT linked.searchResultUpdated();
+        linked.owner.contactModel->searchContact(currentFilter);
+        Q_EMIT linked.filterChanged();
+    }
+
     auto convIds = storage::getConversationsWithPeer(db, fromId);
     if (convIds.empty()) {
         // in case if we receive call after removing contact add conversation request;
         try {
             auto contact = linked.owner.contactModel->getContact(fromId);
-            if (contact.profileInfo.type == profile::Type::PENDING && !contact.isBanned
+            if (!isOutgoing && !contact.isBanned
                 && fromId != linked.owner.profileInfo.uri) {
                 addContactRequest(fromId);
             }
+            if (isOutgoing && contact.profileInfo.type == profile::Type::TEMPORARY) {
+                linked.owner.contactModel->addContact(contact);
+            }
         } catch (const std::out_of_range&) {
         }
     }
 
     auto conversationIndices = getIndicesForContact(fromId);
     if (conversationIndices.empty()) {
-        qDebug() << "ConversationModelPimpl::slotIncomingCall, but conversation not found";
+        qDebug() << "ConversationModelPimpl::slotNewCall, but conversation not found";
         return; // Not a contact
     }
 
diff --git a/src/libclient/qtwrapper/callmanager_wrap.h b/src/libclient/qtwrapper/callmanager_wrap.h
index 7ae7c1fd9845b2e80107fd69a726fb32c630e6cc..26eef875c53234f326196fec3737a324a1653f3f 100644
--- a/src/libclient/qtwrapper/callmanager_wrap.h
+++ b/src/libclient/qtwrapper/callmanager_wrap.h
@@ -123,21 +123,6 @@ public:
                                     QString(callId.c_str()),
                                     QString(from.c_str()));
             }),
-            exportable_callback<CallSignal::IncomingCallWithMedia>(
-                [this](const std::string& accountId,
-                       const std::string& callId,
-                       const std::string& from,
-                       const std::vector<std::map<std::string, std::string>>& mediaList) {
-                    LOG_LIBJAMI_SIGNAL4("incomingCallWithMedia",
-                                        QString(accountId.c_str()),
-                                        QString(callId.c_str()),
-                                        QString(from.c_str()),
-                                        convertVecMap(mediaList));
-                    Q_EMIT incomingCallWithMedia(QString(accountId.c_str()),
-                                                 QString(callId.c_str()),
-                                                 QString(from.c_str()),
-                                                 convertVecMap(mediaList));
-                }),
             exportable_callback<CallSignal::MediaChangeRequested>(
                 [this](const std::string& accountId,
                        const std::string& callId,
@@ -639,10 +624,6 @@ Q_SIGNALS: // SIGNALS
                          const QString& from,
                          const MapStringString& message);
     void incomingCall(const QString& accountId, const QString& callId, const QString& from);
-    void incomingCallWithMedia(const QString& accountId,
-                               const QString& callId,
-                               const QString& from,
-                               const VectorMapStringString& mediaList);
     void mediaChangeRequested(const QString& accountId,
                               const QString& callId,
                               const VectorMapStringString& mediaList);