diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index d42b640de14420888eb5bce802f5f05ce52752bb..e5b768c5ec7bf9487477fdfc64be6c21184add3b 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -27,6 +27,7 @@
 #include "config.h"
 #endif
 
+#include "ringcontact.h"
 #include "configkeys.h"
 
 #include "thread_pool.h"
@@ -153,85 +154,6 @@ RingAccount::TrustRequest {
     MSGPACK_DEFINE_MAP(device, received, payload)
 };
 
-struct RingAccount::Contact
-{
-    /** Time of contact addition */
-    time_t added {0};
-
-    /** Time of contact removal */
-    time_t removed {0};
-
-    /** True if we got confirmation that this contact also added us */
-    bool confirmed {false};
-
-    /** True if the contact is banned (if not active) */
-    bool banned {false};
-
-    /** True if the contact is an active contact (not banned nor removed) */
-    bool isActive() const { return added > removed; }
-    bool isBanned() const { return not isActive() and banned; }
-
-    Contact() = default;
-    Contact(const Json::Value& json) {
-        added = json["added"].asInt();
-        removed = json["removed"].asInt();
-        confirmed = json["confirmed"].asBool();
-        banned = json["banned"].asBool();
-    }
-
-    /**
-     * Update this contact using other known contact information,
-     * return true if contact state was changed.
-     */
-    bool update(const Contact& c) {
-        const auto copy = *this;
-        if (c.added > added) {
-            added = c.added;
-        }
-        if (c.removed > removed) {
-            removed = c.removed;
-            banned = c.banned;
-        }
-        if (c.confirmed != confirmed) {
-            confirmed = c.confirmed or confirmed;
-        }
-        return hasDifferentState(copy);
-    }
-    bool hasDifferentState(const Contact& other) const {
-        return other.isActive() != isActive()
-            or other.isBanned() != isBanned()
-            or other.confirmed  != confirmed;
-    }
-
-    Json::Value toJson() const {
-        Json::Value json;
-        json["added"] = Json::Int64(added);
-        json["removed"] = Json::Int64(removed);
-        json["confirmed"] = confirmed;
-        json["banned"] = banned;
-        return json;
-    }
-
-    std::map<std::string, std::string> toMap() const {
-        if (not (isActive() or isBanned())) {
-            return {};
-        }
-
-        std::map<std::string, std::string> result {
-            {"added", std::to_string(added)}
-        };
-
-        if (isActive())
-            result.emplace("confirmed", confirmed ? TRUE_STR : FALSE_STR);
-        else if (isBanned())
-            result.emplace("banned", TRUE_STR);
-
-        return result;
-    }
-
-    MSGPACK_DEFINE_MAP(added, removed, confirmed, banned)
-};
-
 /**
  * Represents a known device attached to this Ring account
  */
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index 61573471878b41614ff55be3a4b8c90b9de04c5b..ced83ee3c336d78f1d7fdeae33ef0d9816ddbf33 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -68,6 +68,7 @@ namespace dev
 namespace ring {
 
 class IceTransport;
+struct Contact;
 
 class RingAccount : public SIPAccountBase {
     public:
@@ -320,7 +321,6 @@ class RingAccount : public SIPAccountBase {
         struct DeviceAnnouncement;
         struct DeviceSync;
         struct BuddyInfo;
-        struct Contact;
 
         void syncDevices();
         void onReceiveDeviceSync(DeviceSync&& sync);
diff --git a/src/ringdht/ringcontact.h b/src/ringdht/ringcontact.h
new file mode 100644
index 0000000000000000000000000000000000000000..711c86b0eaada8f9abd671a1593aa5ddd30b8c4a
--- /dev/null
+++ b/src/ringdht/ringcontact.h
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 2014-2017 Savoir-faire Linux Inc.
+ *  Author : Adrien Béraud <adrien.beraud@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, see <https://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include "string_utils.h"
+
+#include <msgpack.hpp>
+#include <json/json.h>
+
+#include <map>
+#include <string>
+#include <ctime>
+
+namespace ring {
+
+struct Contact
+{
+    /** Time of contact addition */
+    time_t added {0};
+
+    /** Time of contact removal */
+    time_t removed {0};
+
+    /** True if we got confirmation that this contact also added us */
+    bool confirmed {false};
+
+    /** True if the contact is banned (if not active) */
+    bool banned {false};
+
+    /** True if the contact is an active contact (not banned nor removed) */
+    bool isActive() const { return added > removed; }
+    bool isBanned() const { return not isActive() and banned; }
+
+    Contact() = default;
+    Contact(const Json::Value& json) {
+        added = json["added"].asInt();
+        removed = json["removed"].asInt();
+        confirmed = json["confirmed"].asBool();
+        banned = json["banned"].asBool();
+    }
+
+    /**
+     * Update this contact using other known contact information,
+     * return true if contact state was changed.
+     */
+    bool update(const Contact& c) {
+        const auto copy = *this;
+        if (c.added > added) {
+            added = c.added;
+        }
+        if (c.removed > removed) {
+            removed = c.removed;
+            banned = c.banned;
+        }
+        if (c.confirmed != confirmed) {
+            confirmed = c.confirmed or confirmed;
+        }
+        return hasDifferentState(copy);
+    }
+    bool hasDifferentState(const Contact& other) const {
+        return other.isActive() != isActive()
+            or other.isBanned() != isBanned()
+            or other.confirmed  != confirmed;
+    }
+
+    Json::Value toJson() const {
+        Json::Value json;
+        json["added"] = Json::Int64(added);
+        json["removed"] = Json::Int64(removed);
+        json["confirmed"] = confirmed;
+        json["banned"] = banned;
+        return json;
+    }
+
+    std::map<std::string, std::string> toMap() const {
+        if (not (isActive() or isBanned())) {
+            return {};
+        }
+
+        std::map<std::string, std::string> result {
+            {"added", std::to_string(added)}
+        };
+
+        if (isActive())
+            result.emplace("confirmed", confirmed ? TRUE_STR : FALSE_STR);
+        else if (isBanned())
+            result.emplace("banned", TRUE_STR);
+
+        return result;
+    }
+
+    MSGPACK_DEFINE_MAP(added, removed, confirmed, banned)
+};
+
+}