diff --git a/src/sakia/core/account.py b/src/sakia/core/account.py
index 4b68f8eb336df42c05908c55af156990a1c38a96..287377e672427afae74b86721fb72125060ce59e 100644
--- a/src/sakia/core/account.py
+++ b/src/sakia/core/account.py
@@ -338,261 +338,6 @@ class Account(QObject):
             value += val
         return value
 
-    async def check_registered(self, community):
-        """
-        Checks for the pubkey and the uid of an account in a community
-        :param sakia.core.Community community: The community we check for registration
-        :return: (True if found, local value, network value)
-        """
-        def _parse_uid_certifiers(data):
-            return self.name == data['uid'], self.name, data['uid']
-
-        def _parse_uid_lookup(data):
-            timestamp = BlockUID.empty()
-            found_uid = ""
-            for result in data['results']:
-                if result["pubkey"] == self.pubkey:
-                    uids = result['uids']
-                    for uid_data in uids:
-                        if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp:
-                            timestamp = uid_data["meta"]["timestamp"]
-                            found_uid = uid_data["uid"]
-            return self.name == found_uid, self.name, found_uid
-
-        def _parse_pubkey_certifiers(data):
-            return self.pubkey == data['pubkey'], self.pubkey, data['pubkey']
-
-        def _parse_pubkey_lookup(data):
-            timestamp = BlockUID.empty()
-            found_uid = ""
-            found_result = ["", ""]
-            for result in data['results']:
-                uids = result['uids']
-                for uid_data in uids:
-                    if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp:
-                        timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"])
-                        found_uid = uid_data["uid"]
-                if found_uid == self.name:
-                    found_result = result['pubkey'], found_uid
-            if found_result[1] == self.name:
-                return self.pubkey == found_result[0], self.pubkey, found_result[0]
-            else:
-                return False, self.pubkey, None
-
-        async def execute_requests(parsers, search):
-            tries = 0
-            request = bma.wot.CertifiersOf
-            nonlocal registered
-            #TODO: The algorithm is quite dirty
-            #Multiplying the tries without any reason...
-            while tries < 3 and not registered[0] and not registered[2]:
-                try:
-                    data = await community.bma_access.simple_request(request,
-                                                                          req_args={'search': search})
-                    if data:
-                        registered = parsers[request](data)
-                    tries += 1
-                except errors.DuniterError as e:
-                    if e.ucode in (errors.NO_MEMBER_MATCHING_PUB_OR_UID,
-                                   e.ucode == errors.NO_MATCHING_IDENTITY):
-                        if request == bma.wot.CertifiersOf:
-                            request = bma.wot.Lookup
-                            tries = 0
-                        else:
-                            tries += 1
-                    else:
-                        tries += 1
-                except asyncio.TimeoutError:
-                    tries += 1
-                except ClientError:
-                    tries += 1
-
-        registered = (False, self.name, None)
-        # We execute search based on pubkey
-        # And look for account UID
-        uid_parsers = {
-                    bma.wot.CertifiersOf: _parse_uid_certifiers,
-                    bma.wot.Lookup: _parse_uid_lookup
-                   }
-        await execute_requests(uid_parsers, self.pubkey)
-
-        # If the uid wasn't found when looking for the pubkey
-        # We look for the uid and check for the pubkey
-        if not registered[0] and not registered[2]:
-            pubkey_parsers = {
-                        bma.wot.CertifiersOf: _parse_pubkey_certifiers,
-                        bma.wot.Lookup: _parse_pubkey_lookup
-                       }
-            await execute_requests(pubkey_parsers, self.name)
-
-        return registered
-
-    async def send_selfcert(self, password, community):
-        """
-        Send our self certification to a target community
-
-        :param str password: The account SigningKey password
-        :param community: The community target of the self certification
-        """
-        try:
-            block_data = await community.bma_access.simple_request(bma.blockchain.Current)
-            signed_raw = "{0}{1}\n".format(block_data['raw'], block_data['signature'])
-            block_uid = Block.from_signed_raw(signed_raw).blockUID
-        except errors.DuniterError as e:
-            if e.ucode == errors.NO_CURRENT_BLOCK:
-                block_uid = BlockUID.empty()
-            else:
-                raise
-        selfcert = SelfCertification(PROTOCOL_VERSION,
-                                     community.currency,
-                                     self.pubkey,
-                                     self.name,
-                                     block_uid,
-                                     None)
-        key = SigningKey(self.salt, password)
-        selfcert.sign([key])
-        logging.debug("Key publish : {0}".format(selfcert.signed_raw()))
-
-        responses = await community.bma_access.broadcast(bma.wot.Add, {}, {'identity': selfcert.signed_raw()})
-        result = (False, "")
-        for r in responses:
-            if r.status == 200:
-                result = (True, (await r.json()))
-            elif not result[0]:
-                result = (False, (await r.text()))
-            else:
-                await r.release()
-        if result[0]:
-            (await self.identity(community)).sigdate = block_uid
-        return result
-
-    async def send_membership(self, password, community, mstype):
-        """
-        Send a membership document to a target community.
-        Signal "document_broadcasted" is emitted at the end.
-
-        :param str password: The account SigningKey password
-        :param community: The community target of the membership document
-        :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave
-        """
-        logging.debug("Send membership")
-
-        blockUID = community.network.current_blockUID
-        self_identity = await self._identities_registry.future_find(self.pubkey, community)
-        selfcert = await self_identity.selfcert(community)
-
-        membership = Membership(PROTOCOL_VERSION, community.currency,
-                                selfcert.pubkey, blockUID, mstype, selfcert.uid,
-                                selfcert.timestamp, None)
-        key = SigningKey(self.salt, password)
-        membership.sign([key])
-        logging.debug("Membership : {0}".format(membership.signed_raw()))
-        responses = await community.bma_access.broadcast(bma.blockchain.Membership, {},
-                        {'membership': membership.signed_raw()})
-        result = (False, "")
-        for r in responses:
-            if r.status == 200:
-                result = (True, (await r.json()))
-            elif not result[0]:
-                result = (False, (await r.text()))
-            else:
-                await r.release()
-        return result
-
-    async def certify(self, password, community, pubkey):
-        """
-        Certify an other identity
-
-        :param str password: The account SigningKey password
-        :param sakia.core.community.Community community: The community target of the certification
-        :param str pubkey: The certified identity pubkey
-        """
-        logging.debug("Certdata")
-        blockUID = community.network.current_blockUID
-        try:
-            identity = await self._identities_registry.future_find(pubkey, community)
-            selfcert = await identity.selfcert(community)
-        except LookupFailureError as e:
-            return False, str(e)
-
-        if selfcert:
-            certification = Certification(PROTOCOL_VERSION, community.currency,
-                                          self.pubkey, pubkey, blockUID, None)
-
-            key = SigningKey(self.salt, password)
-            certification.sign(selfcert, [key])
-            signed_cert = certification.signed_raw(selfcert)
-            logging.debug("Certification : {0}".format(signed_cert))
-
-            data = {'cert': certification.signed_raw(selfcert)}
-            logging.debug("Posted data : {0}".format(data))
-            responses = await community.bma_access.broadcast(bma.wot.Certify, {}, data)
-            result = (False, "")
-            for r in responses:
-                if r.status == 200:
-                    result = (True, (await r.json()))
-                    # signal certification to all listeners
-                    self.certification_accepted.emit()
-                elif not result[0]:
-                    result = (False, (await r.text()))
-                else:
-                    await r.release()
-            return result
-        else:
-            return False, self.tr("Could not find user self certification.")
-
-    async def revoke(self, password, community):
-        """
-        Revoke self-identity on server, not in blockchain
-
-        :param str password: The account SigningKey password
-        :param sakia.core.community.Community community: The community target of the revokation
-        """
-        revoked = await self._identities_registry.future_find(self.pubkey, community)
-
-        revokation = Revocation(PROTOCOL_VERSION, community.currency, None)
-        selfcert = await revoked.selfcert(community)
-
-        key = SigningKey(self.salt, password)
-        revokation.sign(selfcert, [key])
-
-        logging.debug("Self-Revokation Document : \n{0}".format(revokation.raw(selfcert)))
-        logging.debug("Signature : \n{0}".format(revokation.signatures[0]))
-
-        data = {
-            'pubkey': revoked.pubkey,
-            'self_': selfcert.signed_raw(),
-            'sig': revokation.signatures[0]
-        }
-        logging.debug("Posted data : {0}".format(data))
-        responses = await community.bma_access.broadcast(bma.wot.Revoke, {}, data)
-        result = (False, "")
-        for r in responses:
-            if r.status == 200:
-                result = (True, (await r.json()))
-            elif not result[0]:
-                result = (False, (await r.text()))
-            else:
-                await r.release()
-        return result
-
-    async def generate_revokation(self, community, password):
-        """
-        Generate account revokation document for given community
-        :param sakia.core.Community community: the community
-        :param str password: the password
-        :return: the revokation document
-        :rtype: duniterpy.documents.certification.Revocation
-        """
-        document = Revocation(PROTOCOL_VERSION, community.currency, self.pubkey, "")
-        identity = await self.identity(community)
-        selfcert = await identity.selfcert(community)
-
-        key = SigningKey(self.salt, password)
-
-        document.sign(selfcert, [key])
-        return document.signed_raw(selfcert)
-
     def start_coroutines(self):
         for c in self.communities:
             c.start_coroutines()
diff --git a/src/sakia/data/connectors/bma.py b/src/sakia/data/connectors/bma.py
index dabb95a5f6f3fc596d58be6ccb1310884a746310..b938903937e416ab23b37d5e67c395b697965fa4 100644
--- a/src/sakia/data/connectors/bma.py
+++ b/src/sakia/data/connectors/bma.py
@@ -40,6 +40,7 @@ class BmaConnector:
         """
         Start a request to the network but don't cache its result.
 
+        :param str currency: the currency requested
         :param class request: A bma request class calling for data
         :param dict req_args: Arguments to pass to the request constructor
         :param dict get_args: Arguments to pass to the request __get__ method
@@ -48,7 +49,6 @@ class BmaConnector:
         nodes = self.filter_nodes(request, self._nodes_processor.synced_nodes(currency))
         if len(nodes) > 0:
             tries = 0
-            json_data = None
             while tries < 3:
                 node = random.choice(nodes)
                 nodes.pop(node)
@@ -60,15 +60,15 @@ class BmaConnector:
                         asyncio.TimeoutError, ValueError, jsonschema.ValidationError) as e:
                     logging.debug(str(e))
                     tries += 1
-        if len(nodes) == 0 or not json_data:
+        if len(nodes) == 0:
             raise NoPeerAvailable("", len(nodes))
-        return json_data
 
     async def broadcast(self, currency, request, req_args={}, post_args={}):
         """
         Broadcast data to a network.
         Sends the data to all knew nodes.
 
+        :param str currency: the currency target
         :param request: A duniterpy bma request class
         :param req_args: Arguments to pass to the request constructor
         :param post_args: Arguments to pass to the request __post__ method
diff --git a/src/sakia/data/entities/identity.py b/src/sakia/data/entities/identity.py
index 1b498694447df29fdd7bd52c2ab2804421ebfb5d..e22e337dc5099571abcac8411bd7c8177e8f1a14 100644
--- a/src/sakia/data/entities/identity.py
+++ b/src/sakia/data/entities/identity.py
@@ -1,5 +1,6 @@
 import attr
-from duniterpy.documents import block_uid, BlockUID
+from duniterpy.documents import block_uid, BlockUID, SelfCertification
+from duniterpy import PROTOCOL_VERSION
 
 
 @attr.s()
@@ -17,3 +18,13 @@ class Identity:
     membership_timestamp = attr.ib(convert=int, default=0, cmp=False, hash=False)
     membership_type = attr.ib(convert=str, default='', validator=lambda s, a, t: t in ('', 'IN', 'OUT'), cmp=False, hash=False)
     membership_written_on = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False, hash=False)
+
+    def self_certification(self):
+        """
+        Creates a self cert document for a given identity
+        :param sakia.data.entities.Identity identity:
+        :return: the document
+        :rtype: duniterpy.documents.SelfCertification
+        """
+        return SelfCertification(PROTOCOL_VERSION, self.currency, self.pubkey,
+                                 self.uid, self.blockstamp, self.signature)
diff --git a/src/sakia/data/processors/identities.py b/src/sakia/data/processors/identities.py
index 2a70ff5891d19e6502a1b9f6706dd3c690882d38..e66a848abe84e117a5a98554bf623ff57ba9626f 100644
--- a/src/sakia/data/processors/identities.py
+++ b/src/sakia/data/processors/identities.py
@@ -4,6 +4,8 @@ from duniterpy.api import bma, errors
 import asyncio
 from aiohttp.errors import ClientError
 from sakia.errors import NoPeerAvailable
+from duniterpy.documents import SelfCertification
+from duniterpy import PROTOCOL_VERSION
 
 
 @attr.s
@@ -55,7 +57,6 @@ class IdentitiesProcessor:
     def update_identity(self, identity):
         """
         Saves an identity state in the db
-        :param identity:
-        :return:
+        :param sakia.data.entities.Identity identity: the identity updated
         """
         self._identities_repo.update(identity)
diff --git a/src/sakia/gui/dialogs/community_cfg/controller.py b/src/sakia/gui/dialogs/community_cfg/controller.py
index d009f236c0c7ab65b83d60e74f0e399278893f4c..31f732cab8e7ea5f0dd8d3b1d8006bbfe6917114 100644
--- a/src/sakia/gui/dialogs/community_cfg/controller.py
+++ b/src/sakia/gui/dialogs/community_cfg/controller.py
@@ -3,7 +3,7 @@ import logging
 from PyQt5.QtGui import QCursor
 from PyQt5.QtWidgets import QDialog, QApplication, QMenu
 from aiohttp.errors import DisconnectedError, ClientError, TimeoutError
-from sakia.tools.exceptions import NoPeerAvailable
+from sakia.errors import NoPeerAvailable
 
 from duniterpy.documents import MalformedDocumentError
 from sakia.decorators import asyncify
diff --git a/src/sakia/gui/password_asker.py b/src/sakia/gui/password_asker.py
index be1b97c3a64a2e4e09ff76e638f8797f94aeb707..adfd850b9444204da817ac840cfcad7379d71e88 100644
--- a/src/sakia/gui/password_asker.py
+++ b/src/sakia/gui/password_asker.py
@@ -10,7 +10,7 @@ import asyncio
 from PyQt5.QtCore import QEvent
 from PyQt5.QtWidgets import QDialog, QMessageBox
 
-from ..gen_resources.password_asker_uic import Ui_PasswordAskerDialog
+from .password_asker_uic import Ui_PasswordAskerDialog
 
 
 class PasswordAskerDialog(QDialog, Ui_PasswordAskerDialog):
diff --git a/src/sakia/services/__init__.py b/src/sakia/services/__init__.py
index fd456d1a052b30a21646849b6ffc0ac653ac798e..2c5869f54cae9a872309cb779f6e553fbdfd8999 100644
--- a/src/sakia/services/__init__.py
+++ b/src/sakia/services/__init__.py
@@ -1,3 +1,4 @@
 from .network import NetworkService
 from .identities import IdentitiesService
 from .blockchain import BlockchainService
+from .documents import DocumentsService
diff --git a/src/sakia/services/documents.py b/src/sakia/services/documents.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fb43d394ab690fe6d7bfdbd8da442dd292c8002
--- /dev/null
+++ b/src/sakia/services/documents.py
@@ -0,0 +1,273 @@
+import asyncio
+import attr
+import logging
+import jsonschema
+from collections import Counter
+
+from duniterpy.key import SigningKey
+from duniterpy import PROTOCOL_VERSION
+from duniterpy.documents import BlockUID, Block, SelfCertification, Certification, Membership, Revocation
+from duniterpy.api import bma, errors
+from sakia.data.entities import Node
+from aiohttp.errors import ClientError, DisconnectedError
+
+
+@attr.s()
+class DocumentsService:
+    """
+    A service to forge and broadcast documents
+    to the network
+    """
+    _bma_connector = attr.ib()  # :type: sakia.data.connectors.BmaConnector
+    _blockchain_processor = attr.ib()  # :type: sakia.data.processors.BlockchainProcessor
+    _identities_processor = attr.ib()  # :type: sakia.data.processors.IdentitiesProcessor
+    _logger = attr.ib(default=lambda: logging.getLogger('sakia'))
+
+    async def check_registered(self, currency):
+        """
+        Checks for the pubkey and the uid of an account in a community
+        :param str currency: The currency we check for registration
+        :return: (True if found, local value, network value)
+        """
+        def _parse_uid_certifiers(data):
+            return self.name == data['uid'], self.name, data['uid']
+
+        def _parse_uid_lookup(data):
+            timestamp = BlockUID.empty()
+            found_uid = ""
+            for result in data['results']:
+                if result["pubkey"] == self.pubkey:
+                    uids = result['uids']
+                    for uid_data in uids:
+                        if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp:
+                            timestamp = uid_data["meta"]["timestamp"]
+                            found_uid = uid_data["uid"]
+            return self.name == found_uid, self.name, found_uid
+
+        def _parse_pubkey_certifiers(data):
+            return self.pubkey == data['pubkey'], self.pubkey, data['pubkey']
+
+        def _parse_pubkey_lookup(data):
+            timestamp = BlockUID.empty()
+            found_uid = ""
+            found_result = ["", ""]
+            for result in data['results']:
+                uids = result['uids']
+                for uid_data in uids:
+                    if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp:
+                        timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"])
+                        found_uid = uid_data["uid"]
+                if found_uid == self.name:
+                    found_result = result['pubkey'], found_uid
+            if found_result[1] == self.name:
+                return self.pubkey == found_result[0], self.pubkey, found_result[0]
+            else:
+                return False, self.pubkey, None
+
+        async def execute_requests(parsers, search):
+            tries = 0
+            request = bma.wot.CertifiersOf
+            nonlocal registered
+            #TODO: The algorithm is quite dirty
+            #Multiplying the tries without any reason...
+            while tries < 3 and not registered[0] and not registered[2]:
+                try:
+                    data = await self._bma_connector.get(currency, request, req_args={'search': search})
+                    if data:
+                        registered = parsers[request](data)
+                    tries += 1
+                except errors.DuniterError as e:
+                    if e.ucode in (errors.NO_MEMBER_MATCHING_PUB_OR_UID,
+                                   e.ucode == errors.NO_MATCHING_IDENTITY):
+                        if request == bma.wot.CertifiersOf:
+                            request = bma.wot.Lookup
+                            tries = 0
+                        else:
+                            tries += 1
+                    else:
+                        tries += 1
+                except asyncio.TimeoutError:
+                    tries += 1
+                except (ClientError, TimeoutError, ConnectionRefusedError, DisconnectedError, ValueError) as e:
+                    self._logger.debug("{0} : {1}".format(str(e), self.node.pubkey[:5]))
+                    self.node.state = Node.OFFLINE
+                except jsonschema.ValidationError as e:
+                    self._logger.debug(str(e))
+                    self._logger.debug("Validation error : {0}".format(self.node.pubkey[:5]))
+                    self.node.state = Node.CORRUPTED
+
+        registered = (False, self.name, None)
+        # We execute search based on pubkey
+        # And look for account UID
+        uid_parsers = {
+                    bma.wot.CertifiersOf: _parse_uid_certifiers,
+                    bma.wot.Lookup: _parse_uid_lookup
+                   }
+        await execute_requests(uid_parsers, self.pubkey)
+
+        # If the uid wasn't found when looking for the pubkey
+        # We look for the uid and check for the pubkey
+        if not registered[0] and not registered[2]:
+            pubkey_parsers = {
+                        bma.wot.CertifiersOf: _parse_pubkey_certifiers,
+                        bma.wot.Lookup: _parse_pubkey_lookup
+                       }
+            await execute_requests(pubkey_parsers, self.name)
+
+        return registered
+
+    async def send_selfcert(self, currency, salt, password):
+        """
+        Send our self certification to a target community
+
+        :param str currency: The currency of the identity
+        :param sakia.data.entities.Identity identity: The certified identity
+        :param str salt: The account SigningKey salt
+        :param str password: The account SigningKey password
+        """
+        try:
+            block_data = await self._bma_connector.get(currency, bma.blockchain.Current)
+            signed_raw = "{0}{1}\n".format(block_data['raw'], block_data['signature'])
+            block_uid = Block.from_signed_raw(signed_raw).blockUID
+        except errors.DuniterError as e:
+            if e.ucode == errors.NO_CURRENT_BLOCK:
+                block_uid = BlockUID.empty()
+            else:
+                raise
+        selfcert = SelfCertification(PROTOCOL_VERSION,
+                                     currency,
+                                     self.pubkey,
+                                     self.name,
+                                     block_uid,
+                                     None)
+        key = SigningKey(self.salt, password)
+        selfcert.sign([key])
+        self._logger.debug("Key publish : {0}".format(selfcert.signed_raw()))
+
+        responses = await self._bma_connector.broadcast(currency, bma.wot.Add, {}, {'identity': selfcert.signed_raw()})
+        result = (False, "")
+        for r in responses:
+            if r.status == 200:
+                result = (True, (await r.json()))
+            elif not result[0]:
+                result = (False, (await r.text()))
+            else:
+                await r.release()
+        return result
+
+    async def send_membership(self, currency, identity, password, mstype):
+        """
+        Send a membership document to a target community.
+        Signal "document_broadcasted" is emitted at the end.
+
+        :param str currency: the currency target
+        :param sakia.data.entities.Identity identity: the identitiy data
+        :param str password: The account SigningKey password
+        :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave
+        """
+        self._logger.debug("Send membership")
+
+        blockUID = self._blockchain_processor.current_buid(currency)
+        membership = Membership(PROTOCOL_VERSION, currency,
+                                identity.pubkey, blockUID, mstype, identity.uid,
+                                identity.timestamp, None)
+        key = SigningKey(self.salt, password)
+        membership.sign([key])
+        self._logger.debug("Membership : {0}".format(membership.signed_raw()))
+        responses = await self._bma_connector.broadcast(currency, bma.blockchain.Membership, {},
+                                                            {'membership': membership.signed_raw()})
+        result = (False, "")
+        for r in responses:
+            if r.status == 200:
+                result = (True, (await r.json()))
+            elif not result[0]:
+                result = (False, (await r.text()))
+            else:
+                await r.release()
+        return result
+
+    async def certify(self, currency, identity, salt, password):
+        """
+        Certify an other identity
+
+        :param str currency: The currency of the identity
+        :param sakia.data.entities.Identity identity: The certified identity
+        :param str salt: The account SigningKey salt
+        :param str password: The account SigningKey password
+        """
+        self._logger.debug("Certdata")
+        blockUID = self._blockchain_processor.current_buid(currency)
+
+        certification = Certification(PROTOCOL_VERSION, currency,
+                                      self.pubkey, identity.pubkey, blockUID, None)
+
+        key = SigningKey(salt, password)
+        certification.sign(identity.self_certification(), [key])
+        signed_cert = certification.signed_raw(identity.self_certification())
+        self._logger.debug("Certification : {0}".format(signed_cert))
+
+        responses = await self._bma_connector.bma_access.broadcast(currency, bma.wot.Certify, {},
+                                                                   {'cert': signed_cert})
+        result = (False, "")
+        for r in responses:
+            if r.status == 200:
+                result = (True, (await r.json()))
+                # signal certification to all listeners
+                self.certification_accepted.emit()
+            elif not result[0]:
+                result = (False, (await r.text()))
+            else:
+                await r.release()
+        return result
+
+    async def revoke(self, currency, identity, salt, password):
+        """
+        Revoke self-identity on server, not in blockchain
+
+        :param str currency: The currency of the identity
+        :param sakia.data.entities.Identity identity: The certified identity
+        :param str salt: The account SigningKey salt
+        :param str password: The account SigningKey password
+        """
+        revocation = Revocation(PROTOCOL_VERSION, currency, None)
+        self_cert = identity.self_certification()
+
+        key = SigningKey(salt, password)
+        revocation.sign(self_cert, [key])
+
+        self._logger.debug("Self-Revokation Document : \n{0}".format(revocation.raw(self_cert)))
+        self._logger.debug("Signature : \n{0}".format(revocation.signatures[0]))
+
+        data = {
+            'pubkey': identity.pubkey,
+            'self_': self_cert.signed_raw(),
+            'sig': revocation.signatures[0]
+        }
+        self._logger.debug("Posted data : {0}".format(data))
+        responses = await self._bma_connector.broadcast(currency, bma.wot.Revoke, {}, data)
+        result = (False, "")
+        for r in responses:
+            if r.status == 200:
+                result = (True, (await r.json()))
+            elif not result[0]:
+                result = (False, (await r.text()))
+            else:
+                await r.release()
+        return result
+
+    async def generate_revokation(self, currency, identity, salt, password):
+        """
+        Generate account revokation document for given community
+
+        :param str currency: The currency of the identity
+        :param sakia.data.entities.Identity identity: The certified identity
+        :param str salt: The account SigningKey salt
+        :param str password: The account SigningKey password
+        """
+        document = Revocation(PROTOCOL_VERSION, currency, identity.pubkey, "")
+        self_cert = identity.self_certification()
+
+        key = SigningKey(salt, password)
+
+        document.sign(self_cert, [key])
+        return document.signed_raw(self_cert)
diff --git a/src/sakia/tests/technical/test_documents_service.py b/src/sakia/tests/technical/test_documents_service.py
new file mode 100644
index 0000000000000000000000000000000000000000..83bec4763205e1606ee1702c230b70a3377735b0
--- /dev/null
+++ b/src/sakia/tests/technical/test_documents_service.py
@@ -0,0 +1,39 @@
+import asyncio
+import unittest
+import sqlite3
+import aiohttp
+from duniterpy.documents import BlockUID, Peer
+from sakia.tests import QuamashTest
+from sakia.services import DocumentsService
+from sakia.data.connectors import NodeConnector, BmaConnector
+from sakia.data.repositories import NodesRepo, MetaDatabase, BlockchainsRepo, IdentitiesRepo
+from sakia.data.processors import NodesProcessor, BlockchainProcessor, IdentitiesProcessor
+
+
+class TestDocumentsService(unittest.TestCase, QuamashTest):
+    def setUp(self):
+        self.setUpQuamash()
+        sqlite3.register_adapter(BlockUID, str)
+        sqlite3.register_adapter(bool, int)
+        sqlite3.register_adapter(list, lambda ls: '\n'.join([str(v) for v in ls]))
+        sqlite3.register_adapter(tuple, lambda ls: '\n'.join([str(v) for v in ls]))
+        sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v)))
+        self.con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
+
+    def tearDown(self):
+        self.tearDownQuamash()
+
+    def test_certify(self):
+        meta_repo = MetaDatabase(self.con)
+        meta_repo.prepare()
+        meta_repo.upgrade_database()
+        nodes_repo = NodesRepo(self.con)
+        nodes_processor = NodesProcessor(nodes_repo)
+        bma_connector = BmaConnector(nodes_processor)
+        blockchain_repo = BlockchainsRepo(self.con)
+        identities_repo = IdentitiesRepo(self.con)
+        blockchain_processor = BlockchainProcessor(blockchain_repo, bma_connector)
+        identities_processor = IdentitiesProcessor(identities_repo, bma_connector)
+        documents_service = DocumentsService(bma_connector, blockchain_processor, identities_processor)
+        #TODO: Build a framework to test documents broadcasting
+