diff --git a/src/jamidht/CMakeLists.txt b/src/jamidht/CMakeLists.txt
index ea9d74b3e5ef10424ee83c20a5c9a90eeaf5c732..733f0070e346de302f18dfb73c02d8c3548209a0 100644
--- a/src/jamidht/CMakeLists.txt
+++ b/src/jamidht/CMakeLists.txt
@@ -5,6 +5,7 @@
 # eth/libdevcore|eth/libdevcrypto
 
 list (APPEND Source_Files__jamidht
+      "${CMAKE_CURRENT_SOURCE_DIR}/abstract_sip_transport.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/account_manager.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/account_manager.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/accountarchive.cpp"
diff --git a/src/jamidht/Makefile.am b/src/jamidht/Makefile.am
index d2c7c3147f556a1b3203f70f963dbe1218567301..c991556d13059cf01a4bc2f3b8c1a1280b830a57 100644
--- a/src/jamidht/Makefile.am
+++ b/src/jamidht/Makefile.am
@@ -12,6 +12,7 @@ libringacc_la_LIBADD = $(DHT_LIBS) \
     ./eth/libdevcrypto/libdevcrypto.la
 
 libringacc_la_SOURCES = \
+        abstract_sip_transport.h \
         jamiaccount.cpp \
         jamiaccount.h \
         connectionmanager.h \
diff --git a/src/jamidht/abstract_sip_transport.h b/src/jamidht/abstract_sip_transport.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f2c2268cd4698ecfff73c6d3afb9cb75ddc338e
--- /dev/null
+++ b/src/jamidht/abstract_sip_transport.h
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include "ip_utils.h"
+#include "sip/sip_utils.h"
+
+#include <pjsip.h>
+#include <pj/pool.h>
+
+namespace jami {
+
+namespace tls {
+
+/**
+ * AbstractSIPTransport
+ *
+ * Implements a pjsip_transport on top
+ */
+class AbstractSIPTransport
+{
+public:
+    using TransportData = struct {
+        pjsip_transport base; // do not move, SHOULD be the fist member
+        AbstractSIPTransport* self {nullptr};
+    };
+    static_assert(std::is_standard_layout<TransportData>::value,
+                  "TranportData requires standard-layout");
+
+    virtual ~AbstractSIPTransport() {};
+
+    virtual pjsip_transport* getTransportBase() = 0;
+
+    virtual IpAddr getLocalAddress() const = 0;
+};
+
+}} // namespace jami::tls
diff --git a/src/jamidht/channeled_transport.cpp b/src/jamidht/channeled_transport.cpp
index 3540e263e6a9f50af435022e70e357d202837004..c30543b6abcb30b19fe9ce704b2c2e3efda16808 100644
--- a/src/jamidht/channeled_transport.cpp
+++ b/src/jamidht/channeled_transport.cpp
@@ -89,18 +89,18 @@ ChanneledSIPTransport::ChanneledSIPTransport(pjsip_endpoint* endpt, int tp_type,
                        pjsip_tx_data *tdata,
                        const pj_sockaddr_t *rem_addr, int addr_len,
                        void *token, pjsip_transport_callback callback) -> pj_status_t {
-        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        auto* this_ = reinterpret_cast<ChanneledSIPTransport*>(reinterpret_cast<TransportData*>(transport)->self);
         return this_->send(tdata, rem_addr, addr_len, token, callback);
     };
     base.do_shutdown = [](pjsip_transport *transport) -> pj_status_t {
-        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        auto* this_ = reinterpret_cast<ChanneledSIPTransport*>(reinterpret_cast<TransportData*>(transport)->self);
         JAMI_DBG("ChanneledSIPTransport@%p {tr=%p {rc=%ld}}: shutdown", this_,
                  transport, pj_atomic_get(transport->ref_cnt));
         if (this_->socket_) this_->socket_->shutdown();
         return PJ_SUCCESS;
     };
     base.destroy = [](pjsip_transport *transport) -> pj_status_t {
-        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        auto* this_ = reinterpret_cast<ChanneledSIPTransport*>(reinterpret_cast<TransportData*>(transport)->self);
         JAMI_DBG("ChanneledSIPTransport@%p: destroying", this_);
         delete this_;
         return PJ_SUCCESS;
@@ -289,4 +289,5 @@ ChanneledSIPTransport::send(pjsip_tx_data* tdata, const pj_sockaddr_t* rem_addr,
     return PJ_EPENDING;
 }
 
+
 }} // namespace jami::tls
diff --git a/src/jamidht/channeled_transport.h b/src/jamidht/channeled_transport.h
index a082f400efd18601ca1d5848194a41474f596210..eb5fd6b06aaca7f8f92deed21d5b470595afec5d 100644
--- a/src/jamidht/channeled_transport.h
+++ b/src/jamidht/channeled_transport.h
@@ -20,13 +20,9 @@
 
 #pragma once
 
-#include "ip_utils.h"
 #include "noncopyable.h"
 #include "scheduled_executor.h"
-#include "sip/sip_utils.h"
-
-#include <pjsip.h>
-#include <pj/pool.h>
+#include "jamidht/abstract_sip_transport.h"
 
 #include <atomic>
 #include <condition_variable>
@@ -49,23 +45,21 @@ namespace tls {
  *
  * Implements a pjsip_transport on top of a ChannelSocket
  */
-class ChanneledSIPTransport
+class ChanneledSIPTransport : public AbstractSIPTransport
 {
 public:
-    using TransportData = struct {
-        pjsip_transport base; // do not move, SHOULD be the fist member
-        ChanneledSIPTransport* self {nullptr};
-    };
-    static_assert(std::is_standard_layout<TransportData>::value,
-                  "TranportData requires standard-layout");
-
     ChanneledSIPTransport(pjsip_endpoint* endpt, int tp_type,
                     const std::shared_ptr<ChannelSocket>& socket,
                     const IpAddr& local, const IpAddr& remote,
                     onShutdownCb&& cb);
     ~ChanneledSIPTransport();
 
-    pjsip_transport* getTransportBase() { return &trData_.base; }
+    pjsip_transport* getTransportBase() override { return &trData_.base; }
+
+    IpAddr getLocalAddress() const override {
+        return local_;
+    }
+
 private:
     NON_COPYABLE(ChanneledSIPTransport);
 
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index cfffc9452b427b2ff89693b2c8d8236c2f0dcb24..fb078ff4448b4aa7d2370f78f86097b75943334f 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -36,6 +36,7 @@
 #include "contact_list.h"
 #include "archive_account_manager.h"
 #include "server_account_manager.h"
+#include "jamidht/channeled_transport.h"
 
 #include "sip/sdp.h"
 #include "sip/sipvoiplink.h"
@@ -322,9 +323,34 @@ JamiAccount::flush()
 }
 
 std::shared_ptr<SIPCall>
-JamiAccount::newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details)
+JamiAccount::newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details, const std::shared_ptr<SipTransport>& sipTr)
 {
     std::lock_guard<std::mutex> lock(callsMutex_);
+
+    if (sipTr) {
+        std::unique_lock<std::mutex> lk(sipConnectionsMtx_);
+        auto sipConnIt = sipConnections_.find(from);
+        if (sipConnIt != sipConnections_.end() && !sipConnIt->second.empty()) {
+            for (auto dit = sipConnIt->second.rbegin(); dit != sipConnIt->second.rend(); ++dit) {
+                for (auto it = dit->second.rbegin(); it != dit->second.rend(); ++it) {
+                    // Search linked Sip Transport
+                    if (it->transport != sipTr) continue;
+
+                    auto call = Manager::instance().callFactory.newCall<SIPCall, JamiAccount>(*this, Manager::instance().getNewCallID(), Call::CallType::INCOMING);
+                    if (!call) return {};
+
+                    std::weak_ptr<SIPCall> wcall = call;
+                    call->setPeerUri(RING_URI_PREFIX + from);
+                    call->setPeerNumber(from);
+
+                    call->updateDetails(details);
+                    return call;
+                }
+            }
+        }
+        lk.unlock();
+    }
+
     auto call_it = pendingSipCalls_.begin();
     while (call_it != pendingSipCalls_.end()) {
         auto call = call_it->call.lock();
@@ -443,10 +469,29 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
     });
 #endif
 
+    // Call connected devices
+    std::set<std::string> devices;
+    std::unique_lock<std::mutex> lk(sipConnectionsMtx_);
+    for (auto deviceConnIt = sipConnections_[toUri].begin(); deviceConnIt != sipConnections_[toUri].end(); ++deviceConnIt) {
+        if (deviceConnIt->second.empty()) continue;
+        auto& it = deviceConnIt->second.back();
+
+        auto transport = it.transport;
+        if (!transport) continue;
+        call->setTransport(transport);
+
+        auto remote_addr = it.channel->underlyingICE()->getRemoteAddress(ICE_COMP_SIP_TRANSPORT);
+        onConnectedOutgoingCall(*call, deviceConnIt->first, remote_addr);
+
+        devices.emplace(deviceConnIt->first);
+    }
+
     // Find listening devices for this account
     dht::InfoHash peer_account(toUri);
-    accountManager_->forEachDevice(peer_account, [this, wCall, toUri, peer_account](const dht::InfoHash& dev)
+    accountManager_->forEachDevice(peer_account, [this, wCall, toUri, peer_account, devices](const dht::InfoHash& dev)
     {
+        // Test if already sent via a SIP transport
+        if (devices.find(dev.toString()) != devices.end()) return;
         auto call = wCall.lock();
         if (not call) return;
         JAMI_DBG("[call %s] calling device %s", call->getCallId().c_str(), dev.toString().c_str());
@@ -2445,9 +2490,9 @@ JamiAccount::getContactHeader(pjsip_transport* t)
 {
     std::string quotedDisplayName = "\"" + displayName_ + "\" " + (displayName_.empty() ? "" : " ");
     if (t) {
-        // FIXME: be sure that given transport is from SipIceTransport
-        auto tlsTr = reinterpret_cast<tls::SipsIceTransport::TransportData*>(t)->self;
-        auto address = tlsTr->getLocalAddress().toString(true);
+        auto* td = reinterpret_cast<tls::AbstractSIPTransport::TransportData*>(t);
+        auto address = td->self->getLocalAddress().toString(true);
+
         contact_.slen = pj_ansi_snprintf(contact_.ptr, PJSIP_MAX_URL_SIZE,
                                          "%s<sips:%s%s%s;transport=dtls>",
                                          quotedDisplayName.c_str(),
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index 9f1230b4b687e4902a4407e11253d5be3afd3aa2..60ff268281d6b800036c945abc47dd9f6977d8b1 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -263,12 +263,13 @@ public:
      * Create incoming SIPCall.
      * @param[in] from The origin of the call
      * @param details use to set some specific details
+     * @param sipTr: current SIP Transport
      * @return std::shared_ptr<T> A shared pointer on the created call.
      *      The type of this instance is given in template argument.
      *      This type can be any base class of SIPCall class (included).
      */
     virtual std::shared_ptr<SIPCall>
-    newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}) override;
+    newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}, const std::shared_ptr<SipTransport>& sipTr = nullptr) override;
 
     virtual bool isTlsEnabled() const override {
         return true;
diff --git a/src/jamidht/sips_transport_ice.cpp b/src/jamidht/sips_transport_ice.cpp
index afff4e0ea9c3c232f2fddebfc6e8f297839df401..964cc579c0d62c5c5a073331b5e94dc8c5e28267 100644
--- a/src/jamidht/sips_transport_ice.cpp
+++ b/src/jamidht/sips_transport_ice.cpp
@@ -183,7 +183,7 @@ SipsIceTransport::SipsIceTransport(pjsip_endpoint* endpt,
                        pjsip_tx_data *tdata,
                        const pj_sockaddr_t *rem_addr, int addr_len,
                        void *token, pjsip_transport_callback callback) -> pj_status_t {
-        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        auto* this_ = reinterpret_cast<SipsIceTransport*>(reinterpret_cast<TransportData*>(transport)->self);
         return this_->send(tdata, rem_addr, addr_len, token, callback);
     };
     base.do_shutdown = [](pjsip_transport *transport) -> pj_status_t {
@@ -706,7 +706,7 @@ SipsIceTransport::send(pjsip_tx_data* tdata, const pj_sockaddr_t* rem_addr,
 }
 
 uint16_t
-SipsIceTransport::getTlsSessionMtu()
+SipsIceTransport::getTlsSessionMtu() const
 {
     return tls_->maxPayload();
 }
diff --git a/src/jamidht/sips_transport_ice.h b/src/jamidht/sips_transport_ice.h
index 13990ef809841a3ce6ceaa1fdf377dd6f212977b..f4b389999849a22f42441f06f85c11249ba60f3e 100644
--- a/src/jamidht/sips_transport_ice.h
+++ b/src/jamidht/sips_transport_ice.h
@@ -22,9 +22,9 @@
 #pragma once
 
 #include "security/tls_session.h"
-#include "ip_utils.h"
 #include "noncopyable.h"
 #include "scheduled_executor.h"
+#include "jamidht/abstract_sip_transport.h"
 
 #include <pjsip.h>
 #include <pj/pool.h>
@@ -54,16 +54,9 @@ namespace jami { namespace tls {
  *
  * Implements TLS transport as an pjsip_transport
  */
-struct SipsIceTransport
+class SipsIceTransport : public AbstractSIPTransport
 {
-    using clock = std::chrono::steady_clock;
-    using TransportData = struct {
-        pjsip_transport base; // do not move, SHOULD be the fist member
-        SipsIceTransport* self {nullptr};
-    };
-    static_assert(std::is_standard_layout<TransportData>::value,
-                  "TranportData requires standard-layout");
-
+public:
     SipsIceTransport(pjsip_endpoint* endpt, int tp_type, const TlsParams& param,
                     const std::shared_ptr<IceTransport>& ice, int comp_id);
     ~SipsIceTransport();
@@ -71,13 +64,13 @@ struct SipsIceTransport
     void shutdown();
 
     std::shared_ptr<IceTransport> getIceTransport() const { return ice_; }
-    pjsip_transport* getTransportBase() { return &trData_.base; }
+    pjsip_transport* getTransportBase() override { return &trData_.base; }
 
-    IpAddr getLocalAddress() const { return local_; }
+    IpAddr getLocalAddress() const override { return local_; }
     IpAddr getRemoteAddress() const { return remote_; }
 
     // uses the tls_ uniquepointer internal gnutls_session_t, to call its method to get its MTU
-    uint16_t getTlsSessionMtu();
+    uint16_t getTlsSessionMtu() const;
 
 private:
     NON_COPYABLE(SipsIceTransport);
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index 0d1342ad634319db00ba8cf16252826c07e1ba58..96798b72c08cfd4df52dd2dad40f658c12103501 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -170,7 +170,7 @@ SIPAccount::~SIPAccount()
 }
 
 std::shared_ptr<SIPCall>
-SIPAccount::newIncomingCall(const std::string& from UNUSED, const std::map<std::string, std::string>& details)
+SIPAccount::newIncomingCall(const std::string& from UNUSED, const std::map<std::string, std::string>& details, const std::shared_ptr<SipTransport>&)
 {
     auto& manager = Manager::instance();
     return manager.callFactory.newCall<SIPCall, SIPAccount>(*this,
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index 4b65318e388f6fda762fb3665e4e643452602de8..8b505ac7201152fa781924bb2a6ea36a4cfe3790 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -515,7 +515,7 @@ class SIPAccount : public SIPAccountBase {
          *      This type can be any base class of SIPCall class (included).
          */
         std::shared_ptr<SIPCall>
-        newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}) override;
+        newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}, const std::shared_ptr<SipTransport>& = nullptr) override;
 
         void onRegister(pjsip_regc_cbparam *param);
 
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index 8e452e84523e0cfa9e6e93d0349dabc93a21b74d..aebe3ac5fb1a0974adfff37fe39158c7324a18b6 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -52,6 +52,8 @@ struct pjmedia_sdp_session;
 
 namespace jami {
 
+class SipTransport;
+
 namespace Conf {
     // SIP specific configuration keys
     const char *const BIND_ADDRESS_KEY = "bindAddress";
@@ -134,7 +136,7 @@ public:
      *      This type can be any base class of SIPCall class (included).
      */
     virtual std::shared_ptr<SIPCall>
-    newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}) = 0;
+    newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}, const std::shared_ptr<SipTransport>& = nullptr) = 0;
 
     virtual bool isStunEnabled() const {
         return false;
diff --git a/src/sip/siptransport.cpp b/src/sip/siptransport.cpp
index bb607ece60743b1ec6587a6a242b3559a850583d..e3aea4ece88d189296bf0f93a3100c4a3913655f 100644
--- a/src/sip/siptransport.cpp
+++ b/src/sip/siptransport.cpp
@@ -163,8 +163,9 @@ SipTransport::removeStateListener(uintptr_t lid)
 uint16_t
 SipTransport::getTlsMtu()
 {
+    auto* td = reinterpret_cast<tls::AbstractSIPTransport::TransportData*>(transport_.get());
     if (isIceTransport_ && isSecure()) {
-        auto tls_tr = reinterpret_cast<tls::SipsIceTransport::TransportData*>(transport_.get())->self;
+        auto* tls_tr = reinterpret_cast<tls::SipsIceTransport*>(td->self);
         return tls_tr->getTlsSessionMtu();
     }
     return 1232; /* Hardcoded yes (it's the IPv6 value).
@@ -440,7 +441,7 @@ SipTransportBroker::getChanneledTransport(const std::shared_ptr<ChannelSocket>&
     auto sips_tr = std::make_unique<tls::ChanneledSIPTransport>(endpt_, type, socket, local, remote, std::move(cb));
     auto tr = sips_tr->getTransportBase();
     auto sip_tr = std::make_shared<SipTransport>(tr);
-    sip_tr->setIsIceTransport();
+    sip_tr->setIsChanneledTransport();
     sips_tr.release(); // managed by PJSIP now
 
     {
diff --git a/src/sip/siptransport.h b/src/sip/siptransport.h
index 7fd04a6d6604bf8ef03ea76723e95f4008bd6edc..ccbfc26c784fdf2fde83fe6afab699d132ee8552 100644
--- a/src/sip/siptransport.h
+++ b/src/sip/siptransport.h
@@ -114,6 +114,7 @@ class SipTransport
         bool isConnected() const noexcept { return connected_; }
 
         void setIsIceTransport() { isIceTransport_ = true; }
+        void setIsChanneledTransport() { isChanneledTransport_ = true; }
 
         uint16_t getTlsMtu();
 
@@ -129,6 +130,7 @@ class SipTransport
 
         bool connected_ {false};
         bool isIceTransport_ {false};
+        bool isChanneledTransport_ {false};
         TlsInfos tlsInfos_;
 };
 
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 7af2660029e83ed48041ada1af884fc2ba5cb91e..00e9b95280c61f9f72f420e98a56b76d863edbe1 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -294,7 +294,8 @@ transaction_request_cb(pjsip_rx_data *rdata)
         }
     }
 
-    auto call = account->newIncomingCall(remote_user, {{"AUDIO_ONLY", (hasVideo ? "false" : "true") }});
+    auto transport = link->sipTransportBroker->addTransport(rdata->tp_info.transport);
+    auto call = account->newIncomingCall(remote_user, {{"AUDIO_ONLY", (hasVideo ? "false" : "true") }}, transport);
     if (!call) {
         return PJ_FALSE;
     }
@@ -303,7 +304,6 @@ transaction_request_cb(pjsip_rx_data *rdata)
     // viaHostname.c_str(), toUsername.c_str(), addrToUse.toString().c_str(), addrSdp.toString().c_str(), peerNumber.c_str());
 
     // Append PJSIP transport to the broker's SipTransport list
-    auto transport = link->sipTransportBroker->addTransport(rdata->tp_info.transport);
     if (!transport) {
         if (not ::strcmp(account->getAccountType(), SIPAccount::ACCOUNT_TYPE)) {
             JAMI_WARN("Using transport from account.");