diff --git a/Cargo.lock b/Cargo.lock
index eaf147a630cd184cff9cc21e6c908704b28b6495..8f4800815053f86edf283e040e7395d0e13a9ef2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -476,6 +476,7 @@ dependencies = [
  "durs-common-tools 0.1.0",
  "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/lib/tools/network-documents/Cargo.toml b/lib/tools/network-documents/Cargo.toml
index d9125a63cf5288ee0afcc6a51b7525de6e0bf0eb..ea1047e0e75036519feb88f87f24aa5daa74f4e7 100644
--- a/lib/tools/network-documents/Cargo.toml
+++ b/lib/tools/network-documents/Cargo.toml
@@ -24,7 +24,8 @@ serde = { version = "1.0.*", features = ["derive"] }
 serde_json = "1.0.*"
 
 [dev-dependencies]
-pretty_assertions = "0.5.1"
 bincode = "1.0.1"
+maplit = "1.0.1"
+pretty_assertions = "0.5.1"
 
 [features]
\ No newline at end of file
diff --git a/lib/tools/network-documents/src/lib.rs b/lib/tools/network-documents/src/lib.rs
index 7cb75283ff74e122c1142a085efffb0d08f95b5d..19d933a5a9234d89d205051ff96a51b5c21c3b59 100644
--- a/lib/tools/network-documents/src/lib.rs
+++ b/lib/tools/network-documents/src/lib.rs
@@ -177,7 +177,7 @@ mod tests {
             EndpointV1::parse_from_raw("WS2P c1c39a0a i3.ifee.fr 80 /ws2p", issuer, 0, 0),
             Ok(EndpointV1 {
                 issuer,
-                api: NetworkEndpointApi(String::from("WS2P")),
+                api: ApiName(String::from("WS2P")),
                 node_id: Some(node_id),
                 hash_full_id: Some(full_id.sha256()),
                 host: String::from("i3.ifee.fr"),
@@ -202,7 +202,7 @@ mod tests {
             EndpointV1::parse_from_raw("WS2P cb06a19b g1.imirhil.fr 53012", issuer, 0, 0),
             Ok(EndpointV1 {
                 issuer,
-                api: NetworkEndpointApi(String::from("WS2P")),
+                api: ApiName(String::from("WS2P")),
                 node_id: Some(node_id),
                 hash_full_id: Some(full_id.sha256()),
                 host: String::from("g1.imirhil.fr"),
diff --git a/lib/tools/network-documents/src/network_endpoint.rs b/lib/tools/network-documents/src/network_endpoint.rs
index 2ceef9ba01a9179dd804f2fce82d5fb0d3ba6dc1..99665280e088559e9389b4d17502c4836043a005 100644
--- a/lib/tools/network-documents/src/network_endpoint.rs
+++ b/lib/tools/network-documents/src/network_endpoint.rs
@@ -21,6 +21,7 @@ use dup_crypto::keys::PubKey;
 use hex;
 use pest::iterators::Pair;
 use pest::Parser;
+use std::collections::HashSet;
 use std::net::{Ipv4Addr, Ipv6Addr};
 use std::str::FromStr;
 
@@ -61,13 +62,41 @@ impl ApiFeatures {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Identifies the API of an endpoint
-pub struct NetworkEndpointApi(pub String);
+pub struct ApiName(pub String);
+
+/// Api version
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ApiVersion(pub usize);
+
+/// Api parts
+#[derive(Clone, Debug)]
+pub struct ApiPart {
+    pub name: ApiName,
+    pub versions: HashSet<ApiVersion>,
+}
+
+impl ApiPart {
+    pub fn union_exist(&self, other: &Self) -> bool {
+        if self.name == other.name {
+            self.versions.intersection(&other.versions).count() > 0
+        } else {
+            false
+        }
+    }
+    pub fn contains(&self, api_name: &ApiName, api_version: ApiVersion) -> bool {
+        if self.name == *api_name {
+            self.versions.contains(&api_version)
+        } else {
+            false
+        }
+    }
+}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Endpoint v1
 pub struct EndpointV1 {
     /// API Name
-    pub api: NetworkEndpointApi,
+    pub api: ApiName,
     /// Node unique identifier
     pub node_id: Option<NodeId>,
     /// Public key of the node declaring this endpoint
@@ -152,7 +181,7 @@ impl EndpointV1 {
         }
         EndpointV1 {
             issuer,
-            api: NetworkEndpointApi(String::from(api_name)),
+            api: ApiName(String::from(api_name)),
             node_id,
             hash_full_id,
             host: String::from(host_str),
@@ -259,7 +288,7 @@ pub struct Endpoint {
 /// Endpoint v2
 pub struct EndpointV2 {
     /// API Name
-    pub api: NetworkEndpointApi,
+    pub api: ApiName,
     /// API version
     pub api_version: u16,
     /// Network features
@@ -395,7 +424,7 @@ impl EndpointV2 {
             network_features = EndpointV2NetworkFeatures(vec![]);
         }
         EndpointV2 {
-            api: NetworkEndpointApi(String::from(api_str)),
+            api: ApiName(String::from(api_str)),
             api_version,
             network_features,
             api_features,
@@ -437,12 +466,18 @@ impl ToString for EndpointEnum {
 
 impl EndpointEnum {
     /// Accessors providing API name
-    pub fn api(&self) -> NetworkEndpointApi {
+    pub fn api(&self) -> ApiName {
         match *self {
             EndpointEnum::V1(ref ep) => ep.api.clone(),
             EndpointEnum::V2(ref ep) => ep.api.clone(),
         }
     }
+    pub fn version(&self) -> ApiVersion {
+        match *self {
+            EndpointEnum::V1(_) => ApiVersion(1),
+            EndpointEnum::V2(_) => ApiVersion(2),
+        }
+    }
     /// Accessors providing node unique identifier
     pub fn node_uuid(&self) -> Option<NodeId> {
         match *self {
@@ -526,6 +561,64 @@ impl EndpointEnum {
 mod tests {
     use super::*;
     use bincode::{deserialize, serialize};
+    use maplit::hashset;
+
+    #[inline]
+    fn api_part_1() -> ApiPart {
+        ApiPart {
+            name: ApiName("api1".to_owned()),
+            versions: hashset![ApiVersion(1)],
+        }
+    }
+
+    #[test]
+    fn test_api_part_contains() {
+        let api_part = api_part_1();
+
+        assert_eq!(
+            true,
+            api_part.contains(&ApiName("api1".to_owned()), ApiVersion(1))
+        );
+
+        assert_eq!(
+            false,
+            api_part.contains(&ApiName("api1".to_owned()), ApiVersion(2))
+        );
+
+        assert_eq!(
+            false,
+            api_part.contains(&ApiName("api2".to_owned()), ApiVersion(1))
+        );
+    }
+
+    #[test]
+    fn test_api_part_union_exist() {
+        let api_part = api_part_1();
+
+        assert_eq!(
+            false,
+            api_part.union_exist(&ApiPart {
+                name: ApiName("api2".to_owned()),
+                versions: hashset![ApiVersion(1)],
+            })
+        );
+
+        assert_eq!(
+            false,
+            api_part.union_exist(&ApiPart {
+                name: ApiName("api1".to_owned()),
+                versions: hashset![ApiVersion(2), ApiVersion(3)],
+            })
+        );
+
+        assert_eq!(
+            true,
+            api_part.union_exist(&ApiPart {
+                name: ApiName("api1".to_owned()),
+                versions: hashset![ApiVersion(1), ApiVersion(2)],
+            })
+        );
+    }
 
     #[test]
     fn test_network_features() {
@@ -575,7 +668,7 @@ mod tests {
     fn test_parse_and_read_minimal_endpoint() {
         let str_endpoint = "UNKNOWN_API 8080";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("UNKNOWN_API")),
+            api: ApiName(String::from("UNKNOWN_API")),
             api_version: 0,
             network_features: EndpointV2NetworkFeatures(vec![]),
             api_features: ApiFeatures(vec![]),
@@ -592,7 +685,7 @@ mod tests {
     fn test_parse_and_read_localhost_endpoint() {
         let str_endpoint = "WS2P localhost 10900";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 0,
             network_features: EndpointV2NetworkFeatures(vec![]),
             api_features: ApiFeatures(vec![]),
@@ -614,7 +707,7 @@ mod tests {
     fn test_parse_and_read_classic_v1_endpoint() {
         let str_endpoint = "ES_CORE_API g1.data.duniter.fr 443";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("ES_CORE_API")),
+            api: ApiName(String::from("ES_CORE_API")),
             api_version: 0,
             network_features: EndpointV2NetworkFeatures(vec![]),
             api_features: ApiFeatures(vec![]),
@@ -631,7 +724,7 @@ mod tests {
     fn test_parse_and_read_endpoint_with_host() {
         let str_endpoint = "WS2P V2 S 7 g1.durs.ifee.fr 443 ws2p";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![4u8]),
             api_features: ApiFeatures(vec![7u8]),
@@ -653,7 +746,7 @@ mod tests {
     fn test_parse_and_read_endpoint_with_ipv4() {
         let str_endpoint = "WS2P V2 S 7 84.16.72.210 443 ws2p";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![4u8]),
             api_features: ApiFeatures(vec![7u8]),
@@ -670,7 +763,7 @@ mod tests {
     fn test_parse_and_read_endpoint_with_ipv6() {
         let str_endpoint = "WS2P V2 S 7 [2001:41d0:8:c5aa::1] 443 ws2p";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![4u8]),
             api_features: ApiFeatures(vec![7u8]),
@@ -687,7 +780,7 @@ mod tests {
     fn test_parse_and_read_endpoint_with_ipv4_and_ip_v6() {
         let str_endpoint = "WS2P V2 S 7 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![4u8]),
             api_features: ApiFeatures(vec![7u8]),
@@ -704,7 +797,7 @@ mod tests {
     fn test_parse_and_read_endpoint_with_all_fields() {
         let str_endpoint = "WS2P V2 S 7 g1.durs.info 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p";
         let endpoint = EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![4u8]),
             api_features: ApiFeatures(vec![7u8]),
diff --git a/lib/tools/network-documents/src/network_peer.rs b/lib/tools/network-documents/src/network_peer.rs
index 53c7cc8f8cb15b29669a88bf592f8fbd3344cf34..148498e3eb0d8deef4466ca2dae38bd1878dc9ce 100644
--- a/lib/tools/network-documents/src/network_peer.rs
+++ b/lib/tools/network-documents/src/network_peer.rs
@@ -272,7 +272,7 @@ mod tests {
 
     fn create_endpoint_v2() -> EndpointV2 {
         EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
@@ -285,7 +285,7 @@ mod tests {
     }
     fn create_second_endpoint_v2() -> EndpointV2 {
         EndpointV2 {
-            api: NetworkEndpointApi(String::from("WS2P")),
+            api: ApiName(String::from("WS2P")),
             api_version: 2,
             network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),