diff --git a/daemon/src/call.h b/daemon/src/call.h
index 13da8e69a3b9a548c282b2fd288ae2fe37e34af7..234244532e51f67826b47aa1629f7dfef1a38a20 100644
--- a/daemon/src/call.h
+++ b/daemon/src/call.h
@@ -253,6 +253,19 @@ class Call : public Recordable {
          */
         virtual void refuse() = 0;
 
+        /**
+         * Transfer a call to specified URI
+         * @param to The recipient of the call
+         */
+        virtual void transfer(const std::string &to) = 0;
+
+        /**
+         * Attended transfer
+         * @param The target call id
+         * @return True on success
+         */
+        virtual bool attendedTransfer(const std::string& to) = 0;
+
     private:
         bool validTransition(CallState newState);
 
diff --git a/daemon/src/iax/iaxcall.cpp b/daemon/src/iax/iaxcall.cpp
index f47b8d56f9af88ef283e93c09fedbaf5e02a149f..56484d7026aff6466d0717c699731f4df562ae7a 100644
--- a/daemon/src/iax/iaxcall.cpp
+++ b/daemon/src/iax/iaxcall.cpp
@@ -166,3 +166,18 @@ IAXCall::refuse()
 
     link_->removeIaxCall(getCallId());
 }
+
+void
+IAXCall::transfer(const std::string& to)
+{
+    std::lock_guard<std::mutex> lock(IAXVoIPLink::mutexIAX);
+    char callto[to.length() + 1];
+    strcpy(callto, to.c_str());
+    iax_transfer(session, callto);
+}
+
+bool
+IAXCall::attendedTransfer(const std::string& /*targetID*/)
+{
+    return false; // TODO
+}
diff --git a/daemon/src/iax/iaxcall.h b/daemon/src/iax/iaxcall.h
index f369c27966a2d4d76ccb85ed24bffa04dcdce7c3..75bdb9b7cbdcbb060f6d54f50afd240cd4d1f8e4 100644
--- a/daemon/src/iax/iaxcall.h
+++ b/daemon/src/iax/iaxcall.h
@@ -86,6 +86,10 @@ class IAXCall : public Call {
 
         void refuse();
 
+        void transfer(const std::string& to);
+
+        bool attendedTransfer(const std::string& to);
+
     private:
         NON_COPYABLE(IAXCall);
 
diff --git a/daemon/src/iax/iaxvoiplink.cpp b/daemon/src/iax/iaxvoiplink.cpp
index 4ee5d1ba92e17ca2e62a1a0a150f0183338c1b25..373b2f06c94338810d2d805d09c97115c888f1aa 100644
--- a/daemon/src/iax/iaxvoiplink.cpp
+++ b/daemon/src/iax/iaxvoiplink.cpp
@@ -371,30 +371,6 @@ IAXVoIPLink::offhold(const std::string& id)
     Manager::instance().startAudioDriverStream();
 }
 
-void
-IAXVoIPLink::transfer(const std::string& id, const std::string& to)
-{
-    char callto[to.length() + 1];
-    strcpy(callto, to.c_str());
-
-    {
-        std::lock_guard<std::mutex> lock(iaxCallMapMutex_);
-        auto call = getIAXCall(id);
-        if (!call)
-            return;
-        {
-            std::lock_guard<std::mutex> lock(mutexIAX);
-            iax_transfer(call->session, callto);
-        }
-    }
-}
-
-bool
-IAXVoIPLink::attendedTransfer(const std::string& /*transferID*/, const std::string& /*targetID*/)
-{
-    return false; // TODO
-}
-
 void
 IAXVoIPLink::carryingDTMFdigits(const std::string& id, char code)
 {
diff --git a/daemon/src/iax/iaxvoiplink.h b/daemon/src/iax/iaxvoiplink.h
index 5a070c71e14f23c6801e9f82e9118a7d5d7810d1..7848e32552a6c0a7a93cc72a4552d097d84ed035 100644
--- a/daemon/src/iax/iaxvoiplink.h
+++ b/daemon/src/iax/iaxvoiplink.h
@@ -149,21 +149,6 @@ class IAXVoIPLink : public VoIPLink {
          */
         virtual void offhold(const std::string& id);
 
-        /**
-         * Transfer a call
-         * @param id The ID of the call
-         * @param to The recipient of the transfer
-         */
-        virtual void transfer(const std::string& id, const std::string& to);
-
-        /**
-         * Perform attended transfer
-         * @param Transfered call ID
-         * @param Target call ID
-         * @return true on success
-         */
-        virtual bool attendedTransfer(const std::string& transferID, const std::string& targetID);
-
         /**
          * Send DTMF
          * @param id The ID of the call
diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp
index 17aa38b82ce58055d8ee54e8d324a79b0be849aa..7db23b04b8655cf0fe0fdca56f8e4e3fa4b939c0 100644
--- a/daemon/src/managerimpl.cpp
+++ b/daemon/src/managerimpl.cpp
@@ -615,10 +615,10 @@ bool ManagerImpl::transferCall(const std::string& callId, const std::string& to)
     } else if (not isConference(getCurrentCallId()))
         unsetCurrentCall();
 
-    std::string accountID(getAccountFromCall(callId));
-    if (accountID.empty())
+    if (auto call = getCallFromCallID(callId))
+        call->transfer(to);
+    else
         return false;
-    getAccountLink(accountID)->transfer(callId, to);
 
     // remove waiting call in case we make transfer without even answer
     removeWaitingCall(callId);
@@ -638,10 +638,9 @@ void ManagerImpl::transferSucceeded()
 
 bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
 {
-    std::string accountid(getAccountFromCall(transferID));
-    if (accountid.empty())
-        return false;
-    return getAccountLink(accountid)->attendedTransfer(transferID, targetID);
+    if (auto call = getCallFromCallID(transferID))
+        return call->attendedTransfer(targetID);
+    return false;
 }
 
 //THREAD=Main : Call:Incoming
diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp
index ae8a9692732746c00b86ee1cf3e11e0ddba05e21..685aa3606fe682592b697e042141489d34dfcd7a 100644
--- a/daemon/src/sip/sipcall.cpp
+++ b/daemon/src/sip/sipcall.cpp
@@ -51,6 +51,10 @@ getSettings()
 static const int INITIAL_SIZE = 16384;
 static const int INCREMENT_SIZE = INITIAL_SIZE;
 
+/** A map to retreive SFLphone internal call id
+ *  Given a SIP call ID (usefull for transaction sucha as transfer)*/
+static std::map<std::string, std::string> transferCallID;
+
 static void
 updateSDPFromSTUN(SIPCall &call, SIPAccount &account, const SipTransport &transport)
 {
@@ -349,3 +353,173 @@ SIPCall::refuse()
 
     siplink.removeSipCall(getCallId());
 }
+
+static void
+transfer_client_cb(pjsip_evsub *sub, pjsip_event *event)
+{
+    auto mod_ua_id = SIPVoIPLink::instance().getMod()->id;
+
+    switch (pjsip_evsub_get_state(sub)) {
+        case PJSIP_EVSUB_STATE_ACCEPTED:
+            if (!event)
+                return;
+
+            pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
+            break;
+
+        case PJSIP_EVSUB_STATE_TERMINATED:
+            pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
+            break;
+
+        case PJSIP_EVSUB_STATE_ACTIVE: {
+            if (!event)
+                return;
+
+            pjsip_rx_data* r_data = event->body.rx_msg.rdata;
+
+            if (!r_data)
+                return;
+
+            std::string request(pjsip_rx_data_get_info(r_data));
+
+            pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) };
+
+            if (!r_data->msg_info.msg)
+                return;
+
+            if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and
+                    request.find("NOTIFY") != std::string::npos) {
+                pjsip_msg_body *body = r_data->msg_info.msg->body;
+
+                if (!body)
+                    return;
+
+                if (pj_stricmp2(&body->content_type.type, "message") or
+                        pj_stricmp2(&body->content_type.subtype, "sipfrag"))
+                    return;
+
+                if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS)
+                    return;
+            }
+
+            if (!r_data->msg_info.cid)
+                return;
+
+            auto call = static_cast<SIPCall *>(pjsip_evsub_get_mod_data(sub, mod_ua_id));
+            if (!call)
+                return;
+
+            if (status_line.code / 100 == 2) {
+                pjsip_tx_data *tdata;
+
+                if (!call->inv)
+                    return;
+
+                if (pjsip_inv_end_session(call->inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS)
+                    pjsip_inv_send_msg(call->inv, tdata);
+
+                Manager::instance().hangupCall(call->getCallId());
+                pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
+            }
+
+            break;
+        }
+
+        default:
+            break;
+    }
+}
+
+bool
+SIPCall::transferCommon(pj_str_t *dst)
+{
+    if (!inv)
+        return false;
+
+    pjsip_evsub_user xfer_cb;
+    pj_bzero(&xfer_cb, sizeof(xfer_cb));
+    xfer_cb.on_evsub_state = &transfer_client_cb;
+
+    pjsip_evsub *sub;
+
+    if (pjsip_xfer_create_uac(inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
+        return false;
+
+    auto& siplink = SIPVoIPLink::instance();
+
+    /* Associate this voiplink of call with the client subscription
+     * We can not just associate call with the client subscription
+     * because after this function, we can no find the cooresponding
+     * voiplink from the call any more. But the voiplink is useful!
+     */
+    pjsip_evsub_set_mod_data(sub, siplink.getMod()->id, this);
+
+    /*
+     * Create REFER request.
+     */
+    pjsip_tx_data *tdata;
+
+    if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
+        return false;
+
+    // Put SIP call id in map in order to retrieve call during transfer callback
+    std::string callidtransfer(inv->dlg->call_id->id.ptr, inv->dlg->call_id->id.slen);
+    transferCallID[callidtransfer] = getCallId();
+
+    /* Send. */
+    if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
+        return false;
+
+    return true;
+}
+
+void
+SIPCall::transfer(const std::string& to)
+{
+    stopRecording();
+
+    std::string account_id(getAccountId());
+    SIPAccount *account = Manager::instance().getSipAccount(account_id);
+
+    if (account == NULL)
+        throw VoipLinkException("Could not find account");
+
+    std::string toUri;
+    pj_str_t dst = { 0, 0 };
+
+    toUri = account->getToUri(to);
+    pj_cstr(&dst, toUri.c_str());
+    DEBUG("Transferring to %.*s", dst.slen, dst.ptr);
+
+    if (!transferCommon(&dst))
+        throw VoipLinkException("Couldn't transfer");
+}
+
+bool
+SIPCall::attendedTransfer(const std::string& /*to*/)
+{
+    if (!inv or !inv->dlg)
+        throw VoipLinkException("Couldn't get invite dialog");
+
+    pjsip_dialog *target_dlg = inv->dlg;
+    pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
+
+    char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' };
+    pj_str_t dst = { str_dest_buf, 1 };
+
+    dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1);
+    dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
+                                 sizeof(str_dest_buf) - dst.slen,
+                                 "?"
+                                 "Replaces=%.*s"
+                                 "%%3Bto-tag%%3D%.*s"
+                                 "%%3Bfrom-tag%%3D%.*s>",
+                                 (int)target_dlg->call_id->id.slen,
+                                 target_dlg->call_id->id.ptr,
+                                 (int)target_dlg->remote.info->tag.slen,
+                                 target_dlg->remote.info->tag.ptr,
+                                 (int)target_dlg->local.info->tag.slen,
+                                 target_dlg->local.info->tag.ptr);
+
+    return transferCommon(&dst);
+}
diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h
index d4332fcdd57465164b2bbb4de3a2d09a6741229f..af4a689ec1868e5e49fab4c2b491a5910b5d9c5c 100644
--- a/daemon/src/sip/sipcall.h
+++ b/daemon/src/sip/sipcall.h
@@ -121,6 +121,10 @@ class SIPCall : public Call {
 
         void refuse();
 
+        void transfer(const std::string& to);
+
+        bool attendedTransfer(const std::string& to);
+
     private:
 
         // override of Call::createHistoryEntry
@@ -129,6 +133,11 @@ class SIPCall : public Call {
 
         NON_COPYABLE(SIPCall);
 
+        /**
+         * Transfer method used for both type of transfer
+         */
+        bool transferCommon(pj_str_t *dst);
+
         /**
          * Audio Rtp Session factory
          */
diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp
index e8d0b62d3ba9d8ee4c6cefbb7ddfa24a072da09b..f79a892185f080352cd87c4611fe04e67a7c3c37 100644
--- a/daemon/src/sip/sipvoiplink.cpp
+++ b/daemon/src/sip/sipvoiplink.cpp
@@ -91,10 +91,6 @@ SIPVoIPLink *SIPVoIPLink::instance_ = nullptr;
 /** Environment variable used to set pjsip's logging level */
 #define SIPLOGLEVEL "SIPLOGLEVEL"
 
-/** A map to retreive SFLphone internal call id
- *  Given a SIP call ID (usefull for transaction sucha as transfer)*/
-static std::map<std::string, std::string> transferCallID;
-
 /**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/
 
 /**
@@ -117,8 +113,6 @@ static void outgoing_request_forked_cb(pjsip_inv_session *inv, pjsip_event *e);
 static void transaction_state_changed_cb(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
 static void registration_cb(pjsip_regc_cbparam *param);
 
-static void transfer_client_cb(pjsip_evsub *sub, pjsip_event *event);
-
 /**
  * Helper function to process refer function on call transfer
  */
@@ -1210,109 +1204,6 @@ SIPVoIPLink::tryGetSIPCall(const std::string& id)
     return call;
 }
 
-bool
-SIPVoIPLink::transferCommon(SIPCall *call, pj_str_t *dst)
-{
-    if (!call or !call->inv)
-        return false;
-
-    pjsip_evsub_user xfer_cb;
-    pj_bzero(&xfer_cb, sizeof(xfer_cb));
-    xfer_cb.on_evsub_state = &transfer_client_cb;
-
-    pjsip_evsub *sub;
-
-    if (pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
-        return false;
-
-    /* Associate this voiplink of call with the client subscription
-     * We can not just associate call with the client subscription
-     * because after this function, we can no find the cooresponding
-     * voiplink from the call any more. But the voiplink is useful!
-     */
-    pjsip_evsub_set_mod_data(sub, mod_ua_.id, this);
-
-    /*
-     * Create REFER request.
-     */
-    pjsip_tx_data *tdata;
-
-    if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
-        return false;
-
-    // Put SIP call id in map in order to retrieve call during transfer callback
-    std::string callidtransfer(call->inv->dlg->call_id->id.ptr, call->inv->dlg->call_id->id.slen);
-    transferCallID[callidtransfer] = call->getCallId();
-
-    /* Send. */
-    if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
-        return false;
-
-    return true;
-}
-
-void
-SIPVoIPLink::transfer(const std::string& id, const std::string& to)
-{
-    auto call = getSipCall(id);
-    if (!call)
-        return;
-
-    call->stopRecording();
-
-    std::string account_id(call->getAccountId());
-    SIPAccount *account = Manager::instance().getSipAccount(account_id);
-
-    if (account == NULL)
-        throw VoipLinkException("Could not find account");
-
-    std::string toUri;
-    pj_str_t dst = { 0, 0 };
-
-    toUri = account->getToUri(to);
-    pj_cstr(&dst, toUri.c_str());
-    DEBUG("Transferring to %.*s", dst.slen, dst.ptr);
-
-    if (!transferCommon(call.get(), &dst))
-        throw VoipLinkException("Couldn't transfer");
-}
-
-bool SIPVoIPLink::attendedTransfer(const std::string& id, const std::string& /*to*/)
-{
-    auto toCall = getSipCall(id);
-    if (!toCall)
-        return false;
-
-    if (!toCall->inv or !toCall->inv->dlg)
-        throw VoipLinkException("Couldn't get invite dialog");
-
-    pjsip_dialog *target_dlg = toCall->inv->dlg;
-    pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
-
-    char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' };
-    pj_str_t dst = { str_dest_buf, 1 };
-
-    dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1);
-    dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
-                                 sizeof(str_dest_buf) - dst.slen,
-                                 "?"
-                                 "Replaces=%.*s"
-                                 "%%3Bto-tag%%3D%.*s"
-                                 "%%3Bfrom-tag%%3D%.*s>",
-                                 (int)target_dlg->call_id->id.slen,
-                                 target_dlg->call_id->id.ptr,
-                                 (int)target_dlg->remote.info->tag.slen,
-                                 target_dlg->remote.info->tag.ptr,
-                                 (int)target_dlg->local.info->tag.slen,
-                                 target_dlg->local.info->tag.ptr);
-
-    auto call = getSipCall(id);
-    if (!call)
-        return false;
-
-    return transferCommon(call.get(), &dst);
-}
-
 static void
 sendSIPInfo(const SIPCall &call, const char *const body, const char *const subtype)
 {
@@ -2173,83 +2064,6 @@ onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata)
     }
 }
 
-static void
-transfer_client_cb(pjsip_evsub *sub, pjsip_event *event)
-{
-    switch (pjsip_evsub_get_state(sub)) {
-        case PJSIP_EVSUB_STATE_ACCEPTED:
-            if (!event)
-                return;
-
-            pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
-            break;
-
-        case PJSIP_EVSUB_STATE_TERMINATED:
-            pjsip_evsub_set_mod_data(sub, mod_ua_.id, NULL);
-            break;
-
-        case PJSIP_EVSUB_STATE_ACTIVE: {
-            SIPVoIPLink *link = static_cast<SIPVoIPLink *>(pjsip_evsub_get_mod_data(sub, mod_ua_.id));
-
-            if (!link or !event)
-                return;
-
-            pjsip_rx_data* r_data = event->body.rx_msg.rdata;
-
-            if (!r_data)
-                return;
-
-            std::string request(pjsip_rx_data_get_info(r_data));
-
-            pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) };
-
-            if (!r_data->msg_info.msg)
-                return;
-
-            if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and
-                    request.find("NOTIFY") != std::string::npos) {
-                pjsip_msg_body *body = r_data->msg_info.msg->body;
-
-                if (!body)
-                    return;
-
-                if (pj_stricmp2(&body->content_type.type, "message") or
-                        pj_stricmp2(&body->content_type.subtype, "sipfrag"))
-                    return;
-
-                if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS)
-                    return;
-            }
-
-            if (!r_data->msg_info.cid)
-                return;
-
-            std::string transferID(r_data->msg_info.cid->id.ptr, r_data->msg_info.cid->id.slen);
-            auto call = SIPVoIPLink::instance().getSipCall(transferCallID[transferID]);
-            if (!call)
-                return;
-
-            if (status_line.code / 100 == 2) {
-                pjsip_tx_data *tdata;
-
-                if (!call->inv)
-                    return;
-
-                if (pjsip_inv_end_session(call->inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS)
-                    pjsip_inv_send_msg(call->inv, tdata);
-
-                Manager::instance().hangupCall(call->getCallId());
-                pjsip_evsub_set_mod_data(sub, mod_ua_.id, NULL);
-            }
-
-            break;
-        }
-
-        default:
-            break;
-    }
-}
-
 static void
 setCallMediaLocal(SIPCall* call, const pj_sockaddr& localIP)
 {
diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h
index 34f70a5ca36624263777c4586041f6b6e0e71465..b797ed54b70d1fd957b297d80097b505f5017398 100644
--- a/daemon/src/sip/sipvoiplink.h
+++ b/daemon/src/sip/sipvoiplink.h
@@ -179,26 +179,6 @@ class SIPVoIPLink : public VoIPLink {
          */
         virtual void offhold(const std::string& id);
 
-        /**
-         * Transfer method used for both type of transfer
-         */
-        bool transferCommon(SIPCall *call, pj_str_t *dst);
-
-        /**
-         * Transfer the call
-         * @param id The call identifier
-         * @param to The recipient of the transfer
-         */
-        virtual void transfer(const std::string& id, const std::string& to);
-
-        /**
-         * Attended transfer
-         * @param The transfered call id
-         * @param The target call id
-         * @return True on success
-         */
-        virtual bool attendedTransfer(const std::string&, const std::string&);
-
         /**
          * Send DTMF refering to account configuration
          * @param id The call identifier
diff --git a/daemon/src/voiplink.h b/daemon/src/voiplink.h
index 18598a93d765760b3e8e034bc8041e9fd019a799..3751c79a83793733558d1d6889cabace08ef3665 100644
--- a/daemon/src/voiplink.h
+++ b/daemon/src/voiplink.h
@@ -113,21 +113,6 @@ class VoIPLink {
          */
         virtual void offhold(const std::string &id) = 0;
 
-        /**
-         * Transfer a call to specified URI
-         * @param id The call identifier
-         * @param to The recipient of the call
-         */
-        virtual void transfer(const std::string &id, const std::string &to) = 0;
-
-        /**
-         * Attended transfer
-         * @param The transfered call id
-         * @param The target call id
-         * @return True on success
-         */
-        virtual bool attendedTransfer(const std::string&, const std::string&) = 0;
-
         /**
          * Send DTMF
          * @param id The call identifier