diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index fb6f168c7d427e54d83dbe7649c23e4be64afffc..84306539ca1da62e31e5ab0ebc34e2e3f0f19806 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -1626,16 +1626,6 @@ RingAccount::registerName(const std::string& /*password*/, const std::string& na
 }
 #endif
 
-void
-RingAccount::handleEvents()
-{
-    // Process DHT events
-    dht_.loop();
-
-    // Call msg in "callto:"
-    handlePendingCallList();
-}
-
 void
 RingAccount::handlePendingCallList()
 {
@@ -1648,11 +1638,9 @@ RingAccount::handlePendingCallList()
         pendingCalls_.clear();
     }
 
-    static const dht::InfoHash invalid_hash; // Invariant
-
     auto pc_iter = std::begin(pending_calls);
     while (pc_iter != std::end(pending_calls)) {
-        bool incoming = pc_iter->call_key == invalid_hash; // do it now, handlePendingCall may invalidate pc data
+        bool incoming = !pc_iter->call_key; // do it now, handlePendingCall may invalidate pc data
         bool handled;
 
         try {
@@ -2101,7 +2089,7 @@ RingAccount::doRegister_()
         config.dht_config.id = identity_;
         config.proxy_server = proxyEnabled_ ? proxyServer_ : std::string();
         config.push_node_id = getAccountID();
-        config.threaded = false;
+        config.threaded = true;
         if (not config.proxy_server.empty())
             RING_WARN("[Account %s] using proxy server %s", getAccountID().c_str(), config.proxy_server.c_str());
 
@@ -2152,7 +2140,7 @@ RingAccount::doRegister_()
 
         dht_.importValues(loadValues());
 
-        Manager::instance().registerEventHandler((uintptr_t)this, [this]{ handleEvents(); });
+        Manager::instance().registerEventHandler((uintptr_t)this, [this]{ handlePendingCallList(); });
         setRegistrationState(RegistrationState::TRYING);
 
         dht_.bootstrap(loadNodes());
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index b2b5ebb5c2e8725e4b399c7fad08f22009c43ead..065d3cf4fe7cf2b38653544fd9f6f13d922162f6 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -439,8 +439,6 @@ class RingAccount : public SIPAccountBase {
 
         const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", std::chrono::hours(24 * 7)};
 
-        void handleEvents();
-
         void startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string toUri);
 
         void onConnectedOutgoingCall(SIPCall& call, const std::string& to_id, IpAddr target);
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index c4c16fa49df509ed6689207903d1271599526410..1e8926e990f34d8d0262713cb4787646fa11d94a 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -597,11 +597,11 @@ SIPVoIPLink::SIPVoIPLink() : pool_(nullptr, pj_pool_release)
     TRY(pjsip_replaces_init_module(endpt_));
 #undef TRY
 
-    // ready to handle events
-    // Implementation note: we don't use std::bind(xxx, this) here
-    // as handleEvents needs a valid instance to be called.
-    Manager::instance().registerEventHandler((uintptr_t)this,
-                                             [this]{ handleEvents(); });
+    sipThread_ = std::thread([this]{
+        sip_utils::register_thread();
+        while (running_)
+            handleEvents();
+    });
 
     RING_DBG("SIPVoIPLink@%p", this);
 }
@@ -610,6 +610,8 @@ SIPVoIPLink::~SIPVoIPLink()
 {
     RING_DBG("~SIPVoIPLink@%p", this);
 
+    running_ = false;
+
     // Remaining calls should not happen as possible upper callbacks
     // may be called and another instance of SIPVoIPLink can be re-created!
 
@@ -626,16 +628,13 @@ SIPVoIPLink::~SIPVoIPLink()
         std::this_thread::sleep_for(std::chrono::seconds(1));
 
     pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), nullptr);
-    Manager::instance().unregisterEventHandler((uintptr_t)this);
-    try {
-        handleEvents();
-    } catch (...) {}
 
     sipTransportBroker.reset();
 
     pjsip_endpt_destroy(endpt_);
     pool_.reset();
     pj_caching_pool_destroy(&cp_);
+    sipThread_.join();
 
     RING_DBG("destroying SIPVoIPLink@%p", this);
 }
@@ -692,11 +691,8 @@ SIPVoIPLink::guessAccount(const std::string& userName,
 void
 SIPVoIPLink::handleEvents()
 {
-    sip_utils::register_thread();
-
-    static const pj_time_val timeout = {0, 0}; // polling
-    auto ret = pjsip_endpt_handle_events(endpt_, &timeout);
-    if (ret != PJ_SUCCESS)
+    const pj_time_val timeout = {10, 0};
+    if (auto ret = pjsip_endpt_handle_events(endpt_, &timeout))
         RING_ERR("pjsip_endpt_handle_events failed with error %s",
                  sip_utils::sip_strerror(ret).c_str());
 
@@ -765,12 +761,7 @@ SIPVoIPLink::dequeKeyframeRequests()
 void
 SIPVoIPLink::requestKeyframe(const std::string &callID)
 {
-    std::shared_ptr<SIPCall> call;
-    const int tries = 10;
-
-    for (int i = 0; !call and i < tries; ++i)
-        call = Manager::instance().callFactory.getCall<SIPCall>(callID); // fixme: need a try version
-
+    auto call = Manager::instance().callFactory.getCall<SIPCall>(callID);
     if (!call)
         return;
 
diff --git a/src/sip/sipvoiplink.h b/src/sip/sipvoiplink.h
index 1dbcdce11554e069315699e608a6df00c0ed9a69..2e02dc7914013b9ea5c4a70676e01309b9e3a9fe 100644
--- a/src/sip/sipvoiplink.h
+++ b/src/sip/sipvoiplink.h
@@ -47,6 +47,8 @@
 #include <mutex>
 #include <memory>
 #include <functional>
+#include <thread>
+#include <atomic>
 
 namespace ring {
 
@@ -161,6 +163,8 @@ class SIPVoIPLink {
 
         mutable pj_caching_pool cp_;
         std::unique_ptr<pj_pool_t, decltype(pj_pool_release)&> pool_;
+        std::atomic_bool running_ {true};
+        std::thread sipThread_;
 
 #ifdef RING_VIDEO
         void dequeKeyframeRequests();