diff --git a/duniterpy/documents/ws2p/heads.py b/duniterpy/documents/ws2p/heads.py
index 97a7a82f5614f9977614201b1afdf2cc46e3bbac..b5b028e1daf01879a3daa7ee68defaedcf8ace9b 100644
--- a/duniterpy/documents/ws2p/heads.py
+++ b/duniterpy/documents/ws2p/heads.py
@@ -1,5 +1,4 @@
 import re
-
 import attr
 
 from ..block_uid import BlockUID
@@ -46,7 +45,7 @@ class Head:
     re_inline = re.compile(WS2P_HEAD_REGEX)
 
     @classmethod
-    def from_inline(cls, inline: str):
+    def from_inline(cls, inline: str, signature: str):
         try:
             data = Head.re_inline.match(inline)
             if data is None:
@@ -62,11 +61,7 @@ class Head:
 
 
 @attr.s()
-class HeadV0:
-    """
-    A document describing a self certification.
-    """
-
+class HeadV0(Head):
     signature = attr.ib(type=str)
     api = attr.ib(type=API)
     head = attr.ib(type=Head)
@@ -92,11 +87,11 @@ class HeadV0:
             if data is None:
                 raise MalformedDocumentError("HeadV0")
             api = API.from_inline(data.group(1))
-            head = Head.from_inline(data.group(2))
+            head = Head.from_inline(data.group(2), "")
             pubkey = data.group(3)
             blockstamp = BlockUID.from_str(data.group(4))
             offload = data.group(5)
-            return cls(signature, api, head, pubkey, blockstamp), offload
+            return cls(head.version, signature, api, head, pubkey, blockstamp), offload
         except AttributeError:
             raise MalformedDocumentError("HeadV0")
 
@@ -106,15 +101,18 @@ class HeadV0:
             for v in attr.astuple(
                 self,
                 recurse=False,
-                filter=attr.filters.exclude(attr.fields(HeadV0).signature),
+                filter=attr.filters.exclude(
+                    attr.fields(HeadV0).version,
+                    attr.fields(HeadV0).signature,
+                    attr.fields(HeadV0).api,
+                ),
             )
         )
-        return ":".join(values)
+        return "{0}:{1}".format(str(self.api), ":".join(values))
 
 
 @attr.s()
-class HeadV1:
-    v0 = attr.ib(type=HeadV0)
+class HeadV1(HeadV0):
     ws2pid = attr.ib(type=str)
     software = attr.ib(type=str)
     software_version = attr.ib(type=str)
@@ -141,35 +139,27 @@ class HeadV1:
             software_version = data.group(3)
             pow_prefix = int(data.group(4))
             offload = data.group(5)
-            return cls(v0, ws2pid, software, software_version, pow_prefix), offload
+            return (
+                cls(
+                    v0.version,
+                    v0.signature,
+                    v0.api,
+                    v0.head,
+                    v0.pubkey,
+                    v0.blockstamp,
+                    ws2pid,
+                    software,
+                    software_version,
+                    pow_prefix,
+                ),
+                offload,
+            )
         except AttributeError:
             raise MalformedDocumentError("HeadV1")
 
-    def inline(self) -> str:
-        values = [
-            str(v)
-            for v in attr.astuple(
-                self, True, filter=attr.filters.exclude(attr.fields(HeadV1).v0)
-            )
-        ]
-        return self.v0.inline() + ":" + ":".join(values)
-
-    @property
-    def pubkey(self) -> str:
-        return self.v0.pubkey
-
-    @property
-    def signature(self) -> str:
-        return self.v0.signature
-
-    @property
-    def blockstamp(self) -> BlockUID:
-        return self.v0.blockstamp
-
 
 @attr.s
-class HeadV2:
-    v1 = attr.ib(type=HeadV1)
+class HeadV2(HeadV1):
     free_member_room = attr.ib(type=int)
     free_mirror_room = attr.ib(type=int)
 
@@ -188,27 +178,22 @@ class HeadV2:
                 raise MalformedDocumentError("HeadV2")
             free_member_room = int(data.group(1))
             free_mirror_room = int(data.group(2))
-            return cls(v1, free_member_room, free_mirror_room), ""
+            return (
+                cls(
+                    v1.version,
+                    v1.signature,
+                    v1.api,
+                    v1.head,
+                    v1.pubkey,
+                    v1.blockstamp,
+                    v1.ws2pid,
+                    v1.software,
+                    v1.software_version,
+                    v1.pow_prefix,
+                    free_member_room,
+                    free_mirror_room,
+                ),
+                "",
+            )
         except AttributeError:
             raise MalformedDocumentError("HeadV2")
-
-    def inline(self) -> str:
-        values = (
-            str(v)
-            for v in attr.astuple(
-                self, True, filter=attr.filters.exclude(attr.fields(HeadV2).v1)
-            )
-        )
-        return self.v1.inline() + ":" + ":".join(values)
-
-    @property
-    def pubkey(self) -> str:
-        return self.v1.pubkey
-
-    @property
-    def signature(self) -> str:
-        return self.v1.signature
-
-    @property
-    def blockstamp(self) -> BlockUID:
-        return self.v1.blockstamp
diff --git a/tests/documents/test_ws2p_heads.py b/tests/documents/test_ws2p_heads.py
index 7dae1ea71f643050618a764b753a3f61e7af1234..fec0c0047514671256f33f05ec71fbdb3bf6b2f5 100644
--- a/tests/documents/test_ws2p_heads.py
+++ b/tests/documents/test_ws2p_heads.py
@@ -9,11 +9,11 @@ headv1_tor = ""
 
 class TestWS2PHeads(unittest.TestCase):
     def test_headv0(self):
-        headv0, _ = HeadV0.from_inline(
-            "WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:"
-            "54813-00000A24802B33B71A91B6E990038C145A4815A45C71E57B2F2EF393183C7E2C",
-            "a1vAAM666kPsMCFTbkgkcCsqHf8nmXR+Lh3D3u+BaXzmArj7kwlItbdGUs4fc9QUG5Lp4TwPS7nhOM5t1Kt6CA==",
-        )
+
+        inline = "WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:54813-00000A24802B33B71A91B6E990038C145A4815A45C71E57B2F2EF393183C7E2C"
+        signature = "a1vAAM666kPsMCFTbkgkcCsqHf8nmXR+Lh3D3u+BaXzmArj7kwlItbdGUs4fc9QUG5Lp4TwPS7nhOM5t1Kt6CA=="
+
+        headv0, _ = HeadV0.from_inline(inline, signature)
 
         self.assertEqual(headv0.api.public, "")
         self.assertEqual(headv0.api.private, "")
@@ -26,28 +26,48 @@ class TestWS2PHeads(unittest.TestCase):
             ),
         )
 
+        new_inline = headv0.inline()
+
+        assert inline == new_inline
+
     def test_ws2p_headv1(self):
-        headv1, _ = HeadV1.from_inline(
-            "WS2POCAIC:HEAD:1:HbTqJ1Ts3RhJ8Rx4XkNyh1oSKmoZL1kY5U7t9mKTSjAB:"
-            "102131-0000066028B991BDFE3FF6DBA84EF519F76B62EA3787BC29D9A05557675B1F16:1152e46e:"
-            "duniter:1.6.21-beta:1",
-            "ZGpT8HG4uX5Hc96gqhzIkkELVjGQKDp2/L+7BTFG5ODxGYWd2VX/H+hdZRqf0iUWRNuhxlequ68kkwMaE6ymBw==",
-        )
+        inline = "WS2P:HEAD:1:HbTqJ1Ts3RhJ8Rx4XkNyh1oSKmoZL1kY5U7t9mKTSjAB:102131-0000066028B991BDFE3FF6DBA84EF519F76B62EA3787BC29D9A05557675B1F16:1152e46e:duniter:1.6.21-beta:1"
+        signature = "ZGpT8HG4uX5Hc96gqhzIkkELVjGQKDp2/L+7BTFG5ODxGYWd2VX/H+hdZRqf0iUWRNuhxlequ68kkwMaE6ymBw=="
+        headv1, _ = HeadV1.from_inline(inline, signature)
 
-        self.assertEqual(headv1.v0.api.public, "IC")
-        self.assertEqual(headv1.v0.api.private, "OCA")
-        self.assertEqual(headv1.v0.head.version, 1)
+        self.assertEqual(headv1.head.version, 1)
         self.assertEqual(headv1.software, "duniter")
         self.assertEqual(headv1.software_version, "1.6.21-beta")
         self.assertEqual(headv1.pow_prefix, 1)
 
+        new_inline = headv1.inline()
+
+        assert inline == new_inline
+
     def test_ws2p_headv2(self):
-        headv2, _ = HeadV2.from_inline(
-            "WS2POCA:HEAD:2:D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH:"
-            "99393-0000017256006BFA979565F1280488D5831DD66054069E46A3EDEB1AECDBBF13:cb36b021:"
-            "duniter:1.6.21:1:20:19",
-            "CgD1vaImPWZUCDFt5HDHUdjCTFcIwW5ndiCx6kXioFLZoz1a4WhCFYXvjI2N8+jEwQdWtf5+yNoHonqBSqirAQ==",
-        )
+        inline = "WS2POCA:HEAD:2:D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH:99393-0000017256006BFA979565F1280488D5831DD66054069E46A3EDEB1AECDBBF13:cb36b021:duniter:1.6.21:1:20:19"
+        signature = "CgD1vaImPWZUCDFt5HDHUdjCTFcIwW5ndiCx6kXioFLZoz1a4WhCFYXvjI2N8+jEwQdWtf5+yNoHonqBSqirAQ=="
+        headv2, _ = HeadV2.from_inline(inline, signature)
+        self.assertEqual(headv2.api.public, "")
+        self.assertEqual(headv2.api.private, "OCA")
         self.assertEqual(headv2.free_member_room, 20)
         self.assertEqual(headv2.free_mirror_room, 19)
-        self.assertEqual(headv2.v1.v0.head.version, 2)
+        self.assertEqual(headv2.head.version, 2)
+
+        new_inline = headv2.inline()
+
+        assert inline == new_inline
+
+        inline = "WS2POCAIC:HEAD:2:D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH:99393-0000017256006BFA979565F1280488D5831DD66054069E46A3EDEB1AECDBBF13:cb36b021:duniter:1.6.21:1:20:19"
+        signature = "CgD1vaImPWZUCDFt5HDHUdjCTFcIwW5ndiCx6kXioFLZoz1a4WhCFYXvjI2N8+jEwQdWtf5+yNoHonqBSqirAQ=="
+
+        headv2, _ = HeadV2.from_inline(inline, signature)
+        self.assertEqual(headv2.api.public, "IC")
+        self.assertEqual(headv2.api.private, "OCA")
+        self.assertEqual(headv2.free_member_room, 20)
+        self.assertEqual(headv2.free_mirror_room, 19)
+        self.assertEqual(headv2.head.version, 2)
+
+        new_inline = headv2.inline()
+
+        assert inline == new_inline