diff --git a/daemon/src/call.cpp b/daemon/src/call.cpp
index dd3356177da3b582b74c2c1a6a18ac1308f64567..0b7dfe679dfa2ac07eb6c84a689941ac61051cf2 100644
--- a/daemon/src/call.cpp
+++ b/daemon/src/call.cpp
@@ -331,7 +331,7 @@ Call::isIceUsed() const
 bool
 Call::isIceRunning() const
 {
-    return iceTransport_->isCompleted();
+    return iceTransport_->isRunning();
 }
 
 ring::IceSocket*
diff --git a/daemon/src/ice_transport.cpp b/daemon/src/ice_transport.cpp
index c0f07097133edc9680d9d6b637d0c278607a524f..0634b03cf1994aeea51fd4eb688a17d02c027f70 100644
--- a/daemon/src/ice_transport.cpp
+++ b/daemon/src/ice_transport.cpp
@@ -338,6 +338,18 @@ IceTransport::isCompleted() const
     return pj_ice_strans_sess_is_complete(icest_.get());
 }
 
+bool
+IceTransport::isRunning() const
+{
+    return pj_ice_strans_get_state(icest_.get()) == PJ_ICE_STRANS_STATE_RUNNING;
+}
+
+bool
+IceTransport::isFailed() const
+{
+    return pj_ice_strans_get_state(icest_.get()) == PJ_ICE_STRANS_STATE_FAILED;
+}
+
 IpAddr
 IceTransport::getLocalAddress(unsigned comp_id) const
 {
diff --git a/daemon/src/ice_transport.h b/daemon/src/ice_transport.h
index a268f0303fe7c131d42404e112a184fb2c831bae..b3c7abaee6a96531b41fbd27df1052f309ec8036 100644
--- a/daemon/src/ice_transport.h
+++ b/daemon/src/ice_transport.h
@@ -103,6 +103,9 @@ class IceTransport {
 
         bool isCompleted() const;
 
+        bool isRunning() const;
+        bool isFailed() const;
+
         IpAddr getLocalAddress(unsigned comp_id) const;
 
         IpAddr getRemoteAddress(unsigned comp_id) const;
diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp
index 775203b3cec7360fa8ef74c47b88303d36c9ac56..fc001770febf62df724ed99d7f45157ddec91b22 100644
--- a/daemon/src/ringdht/ringaccount.cpp
+++ b/daemon/src/ringdht/ringaccount.cpp
@@ -109,11 +109,12 @@ RingAccount::~RingAccount()
 std::shared_ptr<SIPCall>
 RingAccount::newIncomingCall(const std::string& from)
 {
-    auto call_it = pendingCalls_.begin();
-    while (call_it != pendingCalls_.end()) {
-        if ((*call_it)->getPeerNumber() == from) {
-            std::shared_ptr<SIPCall> call = *call_it;
-            pendingCalls_.erase(call_it);
+    std::lock_guard<std::mutex> lock(callsMutex_);
+    auto call_it = pendingSipCalls_.begin();
+    while (call_it != pendingSipCalls_.end()) {
+        if (call_it->call->getPeerNumber() == from) {
+            auto call = call_it->call;
+            pendingSipCalls_.erase(call_it);
             RING_WARN("Found matching call for %s", from.c_str());
             return call;
         } else {
@@ -142,6 +143,7 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
         throw std::invalid_argument("id must be a ring infohash");
 
     RING_DBG("Calling DHT peer %s", toUri.c_str());
+    auto toH = dht::InfoHash(toUri);
 
     auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::OUTGOING);
     call->setIPToIP(true);
@@ -168,13 +170,13 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
     const dht::Value::Id replyvid = callvid+1;
     dht_.putEncrypted(
         callkey,
-        dht::InfoHash(toUri),
+        toH,
         dht::Value {
             ICE_ANNOUCEMENT_TYPE.id,
             ice->getLocalAttributesAndCandidates(),
             callvid
         },
-        [callkey, callvid, call, shared](bool ok){
+        [callkey, callvid, call, shared](bool ok) {
             auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
             if (!ok) {
                 call->setConnectionState(Call::DISCONNECTED);
@@ -186,35 +188,28 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
 
     dht_.listen(
         callkey,
-        [shared, call, callkey, ice, toUri, replyvid] (const std::vector<std::shared_ptr<dht::Value>>& vals) {
+        [shared, call, callkey, ice, toH, replyvid] (const std::vector<std::shared_ptr<dht::Value>>& vals) {
             auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
             RING_DBG("Outcall listen callback (%d values)", vals.size());
             for (const auto& v : vals) {
                 if (v->recipient != this_.dht_.getId() || v->type != this_.ICE_ANNOUCEMENT_TYPE.id) {
-                    RING_WARN("Ignoring non encrypted or bad type value %s.", v->toString().c_str());
+                    RING_DBG("Ignoring non encrypted or bad type value %s.", v->toString().c_str());
                     continue;
                 }
-                if (v->id != replyvid) {
-                    RING_WARN("Ignoring value ID %llx (expected %llx)", v->id, replyvid);
+                if (v->id != replyvid)
                     continue;
-                }
-                RING_WARN("Got a DHT reply from %s !", toUri.c_str());
+                RING_WARN("Got a DHT reply from %s !", toH.toString().c_str());
                 RING_WARN("Performing ICE negotiation.");
                 ice->start(v->data);
-                if (ice->waitForNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) {
-                    call->setConnectionState(Call::DISCONNECTED);
-                    Manager::instance().callFailure(*call);
-                    return false;
-                }
-                call->setTransport(this_.link_->sipTransport->getIceTransport(ice, ICE_COMP_SIP_TRANSPORT));
-                call->setConnectionState(Call::PROGRESSING);
-                this_.createOutgoingCall(call, toUri, ice->getRemoteAddress(ICE_COMP_SIP_TRANSPORT));
                 return false;
             }
             return true;
         }
     );
-
+    {
+        std::lock_guard<std::mutex> lock(callsMutex_);
+        pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, call, toH});
+    }
     return call;
 }
 
@@ -526,6 +521,40 @@ std::map<std::string, std::string> RingAccount::getAccountDetails() const
     return a;
 }
 
+void
+RingAccount::handleEvents()
+{
+    dht_.loop();
+
+    std::lock_guard<std::mutex> lock(callsMutex_);
+    auto now = std::chrono::steady_clock::now();
+    auto c = pendingCalls_.begin();
+    while (c != pendingCalls_.end()) {
+        auto ice = c->ice.get();
+        auto call = c->call.get();
+        if (ice->isRunning()) {
+            call->setTransport(link_->sipTransport->getIceTransport(c->ice, ICE_COMP_SIP_TRANSPORT));
+            call->setConnectionState(Call::PROGRESSING);
+            if (c->id == dht::InfoHash()) {
+                RING_WARN("ICE succeeded : moving incomming call to pending sip call");
+                auto in = c;
+                ++c;
+                pendingSipCalls_.splice(pendingSipCalls_.begin(), pendingCalls_, in, c);
+            } else {
+                RING_WARN("ICE succeeded : removing pending outgoing call");
+                createOutgoingCall(c->call, c->id.toString(), ice->getRemoteAddress(ICE_COMP_SIP_TRANSPORT));
+                c = pendingCalls_.erase(c);
+            }
+        } else if (ice->isFailed() || now - c->start > std::chrono::seconds(ICE_NEGOTIATION_TIMEOUT)) {
+            RING_WARN("ICE timeout : removing pending outgoing call");
+            call->setConnectionState(Call::DISCONNECTED);
+            Manager::instance().callFailure(*call);
+            c = pendingCalls_.erase(c);
+        } else
+            ++c;
+    }
+}
+
 void RingAccount::doRegister()
 {
     if (not isEnabled()) {
@@ -589,7 +618,7 @@ void RingAccount::doRegister()
 
         username_ = dht_.getId().toString();
 
-        Manager::instance().registerEventHandler((uintptr_t)this, [this](){ dht_.loop(); });
+        Manager::instance().registerEventHandler((uintptr_t)this, [this]{ handleEvents(); });
         setRegistrationState(RegistrationState::TRYING);
 
         dht_.bootstrap(loadNodes());
@@ -662,25 +691,22 @@ void RingAccount::doRegister()
                                 reply_vid
                             },
                             [call,ice,shared,listenKey,reply_vid](bool ok) {
-                                RING_WARN("ICE exchange put %d", ok);
                                 auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
                                 this_.dht_.cancelPut(listenKey, reply_vid);
-                                RING_WARN("waitForIceNegociation");
-                                if (!ok || ice->waitForNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) {
-                                    RING_WARN("SIP ICE negotiation failed");
+                                if (!ok) {
+                                    RING_ERR("ICE exchange failed");
                                     call->setConnectionState(Call::DISCONNECTED);
                                     Manager::instance().callFailure(*call);
-                                } else {
-                                    RING_DBG("SIP ICE negotiation succeeded");
-                                    call->setConnectionState(Call::PROGRESSING);
-                                    call->setTransport(this_.link_->sipTransport->getIceTransport(ice, ICE_COMP_SIP_TRANSPORT));
                                 }
                             }
                         );
                         ice->start(v->data);
                         call->setPeerNumber(from);
                         call->initRecFilename(from);
-                        this_.pendingCalls_.push_back(call);
+                        {
+                            std::lock_guard<std::mutex> lock(this_.callsMutex_);
+                            this_.pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, call, dht::InfoHash()});
+                        }
                         return true;
                     } catch (const std::exception& e) {
                         RING_ERR("ICE/DHT error: %s", e.what());
@@ -704,7 +730,11 @@ void RingAccount::doRegister()
 
 void RingAccount::doUnregister(std::function<void(bool)> released_cb)
 {
-    pendingCalls_.clear();
+    {
+        std::lock_guard<std::mutex> lock(callsMutex_);
+        pendingCalls_.clear();
+        pendingSipCalls_.clear();
+    }
     Manager::instance().unregisterEventHandler((uintptr_t)this);
     saveNodes(dht_.exportNodes());
     saveValues(dht_.exportValues());
diff --git a/daemon/src/ringdht/ringaccount.h b/daemon/src/ringdht/ringaccount.h
index d60baaf9dbc23c79080358f8d18caa821531e645..9089c647465c0e8ed59cc9dad3dc57d8a9c9c8b5 100644
--- a/daemon/src/ringdht/ringaccount.h
+++ b/daemon/src/ringdht/ringaccount.h
@@ -254,7 +254,11 @@ class RingAccount : public SIPAccountBase {
     private:
 
         const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", std::chrono::hours(24 * 7)};
-        const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", std::chrono::minutes(15)};
+        const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", std::chrono::minutes(3)};
+
+        NON_COPYABLE(RingAccount);
+
+        void handleEvents();
 
         void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target);
 
@@ -264,8 +268,6 @@ class RingAccount : public SIPAccountBase {
          */
         virtual void setAccountDetails(const std::map<std::string, std::string> &details);
 
-        NON_COPYABLE(RingAccount);
-
         /**
          * Start a SIP Call
          * @param call  The current call
@@ -286,11 +288,22 @@ class RingAccount : public SIPAccountBase {
 
         dht::DhtRunner dht_ {};
 
+        struct PendingCall {
+            std::chrono::steady_clock::time_point start;
+            std::shared_ptr<ring::IceTransport> ice;
+            std::shared_ptr<SIPCall> call;
+            dht::InfoHash id;
+        };
+        /**
+         * DHT calls waiting for negotiation
+         */
+        std::list<PendingCall> pendingCalls_ {};
         /**
-         * Incomming DHT calls that are not yet actual SIP calls.
+         * Incoming DHT calls that are not yet actual SIP calls.
          */
-        std::list<std::shared_ptr<SIPCall>> pendingCalls_ {};
+        std::list<PendingCall> pendingSipCalls_ {};
         std::set<dht::Value::Id> treatedCalls_ {};
+        mutable std::mutex callsMutex_ {};
 
         std::string cacertPath_ {};
         std::string privkeyPath_ {};
diff --git a/daemon/src/ringdht/sip_transport_ice.cpp b/daemon/src/ringdht/sip_transport_ice.cpp
index f3c730dd4e2f4a774a24265696abfe91b68a41cc..d7410981e2906af68dd74b623ac07b770cf8e445 100644
--- a/daemon/src/ringdht/sip_transport_ice.cpp
+++ b/daemon/src/ringdht/sip_transport_ice.cpp
@@ -66,8 +66,8 @@ SipIceTransport::SipIceTransport(pjsip_endpoint* endpt, pj_pool_t& /* pool */,
     , comp_id_(comp_id)
     , destroy_cb_(destroy_cb)
 {
-    if (not ice or not ice->isCompleted())
-        throw std::logic_error("ice transport must exist and negociation completed");
+    if (not ice or not ice->isRunning())
+        throw std::logic_error("ice transport must exist and negotiation completed");
 
     RING_DBG("Creating SipIceTransport");