diff --git a/invenio/modules/uploader/api.py b/invenio/modules/uploader/api.py
index 7d00df7a6..f6c0cf0e0 100644
--- a/invenio/modules/uploader/api.py
+++ b/invenio/modules/uploader/api.py
@@ -1,107 +1,64 @@
 # -*- coding: utf-8 -*-
 ##
 ## This file is part of Invenio.
 ## Copyright (C) 2014 CERN.
 ##
 ## Invenio 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 2 of the
 ## License, or (at your option) any later version.
 ##
 ## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
+"""
+    invenio.modules.uploader.api
+    ----------------------------
+
+    Uploader API.
+
+    Following example shows how to use this API for an easy use case::
+
+        >>> from invenio.modules.uploader.api import run
+        >>> blob = open('./testsuite/data/demo_record_marc_data.xml').read()
+        >>> reader_info = dict(schema='xml')
+        >>> run('insert', blob, master_format='marc', reader_info=reader_info)
+
+"""
+
 from __future__ import print_function
 
 from celery import chord
-from werkzeug.utils import import_string
-from workflow.engine import GenericWorkflowEngine as WorkflowEngine
 
 from invenio.base.globals import cfg
-from invenio.celery import celery
-from invenio.modules.jsonalchemy.reader import Reader, split_blob
-from invenio.modules.records.api import Record
+from invenio.modules.jsonalchemy.reader import split_blob
 
 from . import signals
+from .tasks import translate, run_workflow
 
-
-def run(name, input_file, master_format='marc', force=False, pretend=False,
-        sync=False, reader_info={}, **kwargs):
-    """@todo: Docstring for run.
+def run(name, input_file, master_format='marc', reader_info={}, **kwargs):
+    """Entry point to run any of the modes of the uploader.
 
     :name: @todo
     :input_file: @todo
     :master_format: @todo
-    :force: @todo
-    :pretend: @todo
-    :sync: @todo
     :reader_info: @todo
-    :**kwargs: @todo
-
+    :kwargs: @todo force, pretend, sync
     :returns: @todo
     """
-    signals.uploader_started.send(mode=name, blob=input_file,
-                                  master_format=master_format, force=force,
-                                  pretend=pretend, **kwargs)
+    signals.uploader_started.send(mode=name,
+                                  blob=input_file,
+                                  master_format=master_format,
+                                  **kwargs)
     for chunk in split_blob(input_file, master_format,
                             cfg['UPLOADER_NUMBER_RECORD_PER_WORKER'],
                             **reader_info):
-        chord(_translate.starmap(
+        chord(translate.starmap(
             [(blob, master_format, reader_info) for blob in chunk])
-        )(_run_workflow.s(name=name, force=force, pretend=pretend, **kwargs))
-
-
-@celery.task
-def _translate(blob, master_format, kwargs={}):
-    """@todo: Docstring for _translate.
-
-    :blob: @todo
-    :master_format: @todo
-    :kwargs: @todo
-    :returns: @todo
-
-    """
-    return (blob,
-            Reader.translate(blob, Record, master_format, **kwargs).dumps())
-
-
-@celery.task
-def _run_workflow(records, name, **kwargs):
-    """@todo: Docstring for _run_workflow.
-
-    :records: @todo
-    :name: @todo
-    :**kwargs: @todo
-    :returns: @todo
-
-    """
-    workflow = import_string(cfg['UPLOADER_WORKFLOWS'][name])
-    wfe = WorkflowEngine()
-    wfe.setWorkflow(workflow)
-    wfe.setVar('options', kwargs)
-    records = [(obj[0], Record(json=obj[1])) for obj in records[0]]
-    wfe.process(records)
-    records = [obj[1].get('recid') for obj in records]
-    signals.uploader_finished.send(name=name, result=records, **kwargs)
-    return records
-
-
-@celery.task
-def _error_handler(uuid):
-    """@todo: Docstring for _error_handler.
-
-    :uuid: @todo
-    :returns: @todo
-
-    """
-    result = celery.AsyncResult(uuid)
-    exc = result.get(propagate=False)
-    print('Task %r raised exception: %r\n%r'
-          % (uuid, exc, result.traceback))
-    return None
+        )(run_workflow.s(name=name, **kwargs))
diff --git a/invenio/modules/uploader/tasks.py b/invenio/modules/uploader/tasks.py
index 91b4a4990..8176280f0 100644
--- a/invenio/modules/uploader/tasks.py
+++ b/invenio/modules/uploader/tasks.py
@@ -1,208 +1,101 @@
 # -*- coding: utf-8 -*-
 ##
 ## This file is part of Invenio.
 ## Copyright (C) 2014 CERN.
 ##
 ## Invenio 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 2 of the
 ## License, or (at your option) any later version.
 ##
 ## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 """
-Core Uploader tasks
-===================
+    invenio.modules.uploader.tasks
+    ------------------------------
 
-Those are the main/common tasks that the uploader will use, they are used inside
-the workflows defined in the ``worflows`` folder.
+    Uploader celery tasks.
 """
 
+import six
+from werkzeug.utils import import_string
+from workflow.engine import GenericWorkflowEngine as WorkflowEngine
+
 from invenio.base.globals import cfg
-from invenio.modules.pidstore.models import PersistentIdentifier
+from invenio.celery import celery
+from invenio.modules.jsonalchemy.reader import Reader
+from invenio.modules.records.api import Record
 
-# Allow celery to collect uploader tasks
-from .api import _translate, _run_workflow, _error_handler  # pylint: disable=W0611
-from .errors import UploaderWorkflowException
+from . import signals
 
 
-def raise_(ex):
-    """
-    Helper task to raise an exception.
-    """
-    def _raise_(obj, eng):
-        raise ex
-    return _raise_
+@celery.task
+def translate(blob, master_format, kwargs={}):
+    """Translate from the `master_format` to `JSON`.
 
+    :blob: String contain the input file.
+    :master_format: Format of the blob, it will used to decide which reader to
+        use.
+    :kwargs: Arguments to be used by the reader.
+        See :class:`invenio.modules.jsonalchemy.reader:Reader`
+    :returns: The blob and the `JSON` representation of the input file created
+        by the reader.
 
-def validate(step):
     """
-    Validate the record using the `validate` method present in each record and
-    the validation mode, either from the command line `options` or from
-    `UPLOADER_VALIDATION_MODE`.
+    return (blob,
+            Reader.translate(blob, Record, master_format, **kwargs).dumps())
 
-    The for the validation the `schema` information from the field definition
-    is used, see `invenio.modules.jsonalchemy.jsonext.parsers.schema_parser`
-    """
-    def _validate(obj, eng):
-        record = obj[1]
-        mode = eng.getVar('options', {}).get('validation_mode',
-                                             cfg['UPLOADER_VALIDATION_MODE'])
-        eng.log.info("Validating record using mode: '%s'", (mode, ))
-        if not hasattr(record, 'validate'):
-            raise UploaderWorkflowException(
-                step, msg="An 'validate' method is needed")
-
-        validator_errors = record.validate()
-        eng.log.info('Validation errors: %s' % (str(validator_errors), ))
-        if mode.lower() == 'strict' and validator_errors:
-            raise UploaderWorkflowException(
-                step, msg="One or more validation errors have occurred, please "
-                          " check them or change the 'validation_mode' to "
-                          "'permissive'.\n%s" % (str(validator_errors), ))
-        eng.log.info('Finish validating the current record')
-    return _validate
-
-
-def retrieve_record_id_from_pids(step):
-    """
-    Retrieve the record identifier from a record by using all the persistent
-    identifiers present in the record.
 
-    If any PID matches with the any in DB then, the record id found is set to
-    the current `record`
-    """
-    def _retrieve_record_id_from_pids(obj, eng):
-        record = obj[1]
-        eng.log.info('Look for PIDs inside the current record')
-        if not hasattr(record, 'persistent_identifiers'):
-            raise UploaderWorkflowException(
-                step, msg="An 'persistent_identifiers' method is needed")
-
-        for pid_name, pid_values in record.persistent_identifiers:
-            eng.log.info("Found PID '%s' with value '%s', trying to match it",
-                         (pid_name, pid_values))
-            matching_recids = set()
-            for possible_pid in pid_values:
-                eng.log.info("Looking for PID %s", (possible_pid, ))
-                pid = PersistentIdentifier.get(
-                    possible_pid.get('type'), possible_pid.get('value'),
-                    possible_pid.get('provider'))
-                if pid:
-                    eng.log.inf("PID found in the data base %s",
-                                (pid.object_value, ))
-                    matching_recids.add(pid.object_value)
-            if len(matching_recids) > 1:
-                raise UploaderWorkflowException(
-                    step, msg="Multiple records found in the database, %s that "
-                              "match '%s'" % (repr(matching_recids), pid_name))
-            elif matching_recids:
-                record['recid'] = matching_recids.pop()
-                eng.log.info(
-                    'Finish looking for PIDs inside the current record')
-                break
-        eng.log.info('Finish looking for PIDs inside the current record')
-    return _retrieve_record_id_from_pids
-
-
-def reserve_record_id(step):
-    """
-    Reserve a new record id for the current object and add it to
-    `record['recid']`.
-    """
-    # TODO: manage exceptions in a better way
-    def _reserve_record_id(obj, eng):
-        record = obj[1]
-        eng.log.info('Reserve a recid for the new record')
-        try:
-            pid = PersistentIdentifier.create('recid', pid_value=None,
-                                              pid_provider='invenio')
-            record['recid'] = int(pid.pid_value)
-            pid.reserve()
-            eng.log.info("Finish reserving a recid '%s' for the new record",
-                         (pid.pid_value, ))
-        except Exception as e:
-            raise UploaderWorkflowException(step, e.message)
-    return _reserve_record_id
-
-
-def save_record(step):
-    """
-    Save the record to the DB using the `_save` method from it.
-    """
-    def _save(obj, eng):
-        record = obj[1]
-        eng.log.info('Saving record to DB')
-        if not hasattr(record, '_save'):
-            raise UploaderWorkflowException(
-                step, msg="An '_save' method is needed")
-        try:
+@celery.task
+def run_workflow(records, name, **kwargs):
+    """Run the uploader workflow itself.
 
-            record._save()
-            eng.log.info('Record saved to DB')
-        except Exception as e:
-            raise UploaderWorkflowException(step, e.message)
-    return _save
+    :records: List of tuples `(blob, json_record)` from :func:`translate`.
+    :name: Name of the workflow to be run.
+    :kwargs: Additional arguments to be used by the tasks of the workflow.
+    :returns: Typically the list of record Ids that has been process, although
+        this value could be modify by the `post_tasks`.
 
+    """
+    def _run_pre_post_tasks(tasks):
+        """Helper function to run list of functions"""
+        for task in tasks:
+            task(records, **kwargs)
+
+    #FIXME: don't know why this is needed but IT IS!
+    records = records[0]
+
+    workflow = import_string(cfg['UPLOADER_WORKFLOWS'][name])
+    _run_pre_post_tasks(workflow['pre_tasks'])
+    tasks = workflow['tasks']
+    wfe = WorkflowEngine()
+    wfe.setWorkflow(tasks)
+    wfe.setVar('options', kwargs)
+    wfe.process(records)
+    _run_pre_post_tasks(workflow['post_tasks'])
+    signals.uploader_finished.send(name=name, result=records, **kwargs)
+    return records
+
+
+# @celery.task
+# def error_handler(uuid):
+#     """@todo: Docstring for _error_handler.
+#
+#     :uuid: @todo
+#     :returns: @todo
+#
+#     """
+#     result = celery.AsyncResult(uuid)
+#     exc = result.get(propagate=False)
+#     print('Task %r raised exception: %r\n%r'
+#           % (uuid, exc, result.traceback))
+#     return None
 
-def save_master_format(step):
-    """@todo: Docstring for save_master_format.
-
-    :step: @todo
-    :returns: @todo
 
-    """
-    def _save_master_format(obj, eng):
-        from invenio.base.helpers import utf8ifier
-        from invenio.modules.formatter.models import Bibfmt
-        from invenio.ext.sqlalchemy import db
-        from zlib import compress
-        eng.log.info('Saving master record to DB')
-        bibfmt = Bibfmt(id_bibrec=obj[1]['recid'],
-                        format=obj[1].additional_info.master_format,
-                        kind='master',
-                        last_updated=obj[1]['modification_date'],
-                        value=compress(utf8ifier(obj[0])))
-        db.session.add(bibfmt)
-        db.session.commit()
-        eng.log.info('Master record saved to DB')
-    return _save_master_format
-
-
-def update_pidstore(step):
-    """
-    Save each PID present in the record to the `PersistentIdentifier` data
-    storage.
-    """
-    # TODO: manage exceptions
-    def _update_pidstore(obj, eng):
-        record = obj[1]
-        eng.log.info('Look for PIDs inside the current record and register '
-                     'them in the DB')
-        if not hasattr(record, 'persistent_identifiers'):
-            raise UploaderWorkflowException(
-                step, msg="An 'persistent_identifiers' method is needed")
-
-        for pid_name, pid_values in record.persistent_identifiers:
-            eng.log.info("Found PID '%s'", (pid_name, ))
-            for pid_value in pid_values:
-                pid = PersistentIdentifier.get(
-                    pid_value.get('type'), pid_value.get('value'),
-                    pid_value.get('provider'))
-                if pid is None:
-                    pid = PersistentIdentifier.create(
-                        pid_value.get('type'), pid_value.get('value'),
-                        pid_value.get('provider'))
-                if not pid.has_object('rec', record['recid']):
-                    pid.assign('rec', record['recid'])
-        eng.log.info('Finish looking for PIDs inside the current record and '
-                     'register them in the DB')
-
-    return _update_pidstore
diff --git a/invenio/modules/uploader/tasks.py b/invenio/modules/uploader/uploader_tasks.py
similarity index 85%
copy from invenio/modules/uploader/tasks.py
copy to invenio/modules/uploader/uploader_tasks.py
index 91b4a4990..9f1d1c6af 100644
--- a/invenio/modules/uploader/tasks.py
+++ b/invenio/modules/uploader/uploader_tasks.py
@@ -1,208 +1,243 @@
 # -*- coding: utf-8 -*-
 ##
 ## This file is part of Invenio.
 ## Copyright (C) 2014 CERN.
 ##
 ## Invenio 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 2 of the
 ## License, or (at your option) any later version.
 ##
 ## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 """
-Core Uploader tasks
-===================
+    invenio.modules.uploader.uploader_tasks
+    ---------------------------------------
 
-Those are the main/common tasks that the uploader will use, they are used inside
-the workflows defined in the ``worflows`` folder.
+    Those are the main/common tasks that the uploader will use, they are used
+    inside the workflows defined in
+    :py:mod:`~invenio.modules.uploader.worflows`.
 """
 
 from invenio.base.globals import cfg
 from invenio.modules.pidstore.models import PersistentIdentifier
+from invenio.modules.records.api import Record
 
-# Allow celery to collect uploader tasks
-from .api import _translate, _run_workflow, _error_handler  # pylint: disable=W0611
 from .errors import UploaderWorkflowException
 
+###########################################################
+##############          Pre tasks         #################
+###########################################################
+
+def create_records_for_workflow(records, **kwargs):
+    """@todo: Docstring for create_records_for_workflow.
+
+    :records: @todo
+    :kwargs: @todo
+    :returns: @todo
+
+    """
+    for i, obj in enumerate(records):
+        records[i] = (obj[0], Record(json=obj[1]))
+
+###########################################################
+##############          Post tasks        #################
+###########################################################
+
+def return_recordids_only(records, **kwargs):
+    """@todo: Docstring for return_recordids_only.
+
+    :records: @todo
+    :kwargs: @todo
+    :returns: @todo
+
+    """
+    for i, obj in enumerate(records):
+        records[i] = obj[1].get('recid')
+
+
+###########################################################
+##############        Workflow tasks      #################
+###########################################################
+
 
 def raise_(ex):
     """
     Helper task to raise an exception.
     """
     def _raise_(obj, eng):
         raise ex
     return _raise_
 
 
 def validate(step):
     """
     Validate the record using the `validate` method present in each record and
     the validation mode, either from the command line `options` or from
     `UPLOADER_VALIDATION_MODE`.
 
     The for the validation the `schema` information from the field definition
     is used, see `invenio.modules.jsonalchemy.jsonext.parsers.schema_parser`
     """
     def _validate(obj, eng):
         record = obj[1]
         mode = eng.getVar('options', {}).get('validation_mode',
                                              cfg['UPLOADER_VALIDATION_MODE'])
         eng.log.info("Validating record using mode: '%s'", (mode, ))
         if not hasattr(record, 'validate'):
             raise UploaderWorkflowException(
                 step, msg="An 'validate' method is needed")
 
         validator_errors = record.validate()
         eng.log.info('Validation errors: %s' % (str(validator_errors), ))
         if mode.lower() == 'strict' and validator_errors:
             raise UploaderWorkflowException(
                 step, msg="One or more validation errors have occurred, please "
                           " check them or change the 'validation_mode' to "
                           "'permissive'.\n%s" % (str(validator_errors), ))
         eng.log.info('Finish validating the current record')
     return _validate
 
 
 def retrieve_record_id_from_pids(step):
     """
     Retrieve the record identifier from a record by using all the persistent
     identifiers present in the record.
 
     If any PID matches with the any in DB then, the record id found is set to
     the current `record`
     """
     def _retrieve_record_id_from_pids(obj, eng):
         record = obj[1]
         eng.log.info('Look for PIDs inside the current record')
         if not hasattr(record, 'persistent_identifiers'):
             raise UploaderWorkflowException(
                 step, msg="An 'persistent_identifiers' method is needed")
 
         for pid_name, pid_values in record.persistent_identifiers:
             eng.log.info("Found PID '%s' with value '%s', trying to match it",
                          (pid_name, pid_values))
             matching_recids = set()
             for possible_pid in pid_values:
                 eng.log.info("Looking for PID %s", (possible_pid, ))
                 pid = PersistentIdentifier.get(
                     possible_pid.get('type'), possible_pid.get('value'),
                     possible_pid.get('provider'))
                 if pid:
                     eng.log.inf("PID found in the data base %s",
                                 (pid.object_value, ))
                     matching_recids.add(pid.object_value)
             if len(matching_recids) > 1:
                 raise UploaderWorkflowException(
                     step, msg="Multiple records found in the database, %s that "
                               "match '%s'" % (repr(matching_recids), pid_name))
             elif matching_recids:
                 record['recid'] = matching_recids.pop()
                 eng.log.info(
                     'Finish looking for PIDs inside the current record')
                 break
         eng.log.info('Finish looking for PIDs inside the current record')
     return _retrieve_record_id_from_pids
 
 
 def reserve_record_id(step):
     """
     Reserve a new record id for the current object and add it to
     `record['recid']`.
     """
     # TODO: manage exceptions in a better way
     def _reserve_record_id(obj, eng):
         record = obj[1]
         eng.log.info('Reserve a recid for the new record')
         try:
             pid = PersistentIdentifier.create('recid', pid_value=None,
                                               pid_provider='invenio')
             record['recid'] = int(pid.pid_value)
             pid.reserve()
             eng.log.info("Finish reserving a recid '%s' for the new record",
                          (pid.pid_value, ))
         except Exception as e:
             raise UploaderWorkflowException(step, e.message)
     return _reserve_record_id
 
 
 def save_record(step):
     """
     Save the record to the DB using the `_save` method from it.
     """
     def _save(obj, eng):
         record = obj[1]
         eng.log.info('Saving record to DB')
         if not hasattr(record, '_save'):
             raise UploaderWorkflowException(
                 step, msg="An '_save' method is needed")
         try:
 
             record._save()
             eng.log.info('Record saved to DB')
         except Exception as e:
             raise UploaderWorkflowException(step, e.message)
     return _save
 
 
 def save_master_format(step):
     """@todo: Docstring for save_master_format.
 
     :step: @todo
     :returns: @todo
 
     """
     def _save_master_format(obj, eng):
         from invenio.base.helpers import utf8ifier
         from invenio.modules.formatter.models import Bibfmt
         from invenio.ext.sqlalchemy import db
         from zlib import compress
         eng.log.info('Saving master record to DB')
         bibfmt = Bibfmt(id_bibrec=obj[1]['recid'],
                         format=obj[1].additional_info.master_format,
                         kind='master',
                         last_updated=obj[1]['modification_date'],
                         value=compress(utf8ifier(obj[0])))
         db.session.add(bibfmt)
         db.session.commit()
         eng.log.info('Master record saved to DB')
     return _save_master_format
 
 
 def update_pidstore(step):
     """
     Save each PID present in the record to the `PersistentIdentifier` data
     storage.
     """
     # TODO: manage exceptions
     def _update_pidstore(obj, eng):
         record = obj[1]
         eng.log.info('Look for PIDs inside the current record and register '
                      'them in the DB')
         if not hasattr(record, 'persistent_identifiers'):
             raise UploaderWorkflowException(
                 step, msg="An 'persistent_identifiers' method is needed")
 
         for pid_name, pid_values in record.persistent_identifiers:
             eng.log.info("Found PID '%s'", (pid_name, ))
             for pid_value in pid_values:
                 pid = PersistentIdentifier.get(
                     pid_value.get('type'), pid_value.get('value'),
                     pid_value.get('provider'))
                 if pid is None:
                     pid = PersistentIdentifier.create(
                         pid_value.get('type'), pid_value.get('value'),
                         pid_value.get('provider'))
                 if not pid.has_object('rec', record['recid']):
                     pid.assign('rec', record['recid'])
         eng.log.info('Finish looking for PIDs inside the current record and '
                      'register them in the DB')
 
     return _update_pidstore
diff --git a/invenio/modules/uploader/workflows/__init__.py b/invenio/modules/uploader/workflows/__init__.py
index e69de29bb..ff5950786 100644
--- a/invenio/modules/uploader/workflows/__init__.py
+++ b/invenio/modules/uploader/workflows/__init__.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+##
+## This file is part of Invenio.
+## Copyright (C) 2014 CERN.
+##
+## Invenio 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 2 of the
+## License, or (at your option) any later version.
+##
+## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+"""
+    invenio.modules.uploader.workflows
+    ----------------------------------
+
+    Every uploader workflow should be a python dictionary contain three keys:
+
+    * `pre_trasks`
+        list of tasks which will be run before running the actual
+        workflow, each element of the list should a callable.
+
+    * `tasks`
+        List of tasks to be run by the `WorkflowEngine`.
+
+    * `post_tasks`
+        Same as for `pre_tasks` but in this case they will be run
+        after the workflow is done.
+
+    An example function to be called after the workflow could be::
+
+        def return_recids_only(records, **kwargs):
+            records = [obj[1].get('recid') for obj in records]
+
+
+    This functions must have always the same parameters (like the one above) and
+    those parameters have the value that
+    :func:`~invenio.modules.uploader.tasks.run_workflow` gets.
+"""
+
diff --git a/invenio/modules/uploader/workflows/insert.py b/invenio/modules/uploader/workflows/insert.py
index eee11e512..fcddc530e 100644
--- a/invenio/modules/uploader/workflows/insert.py
+++ b/invenio/modules/uploader/workflows/insert.py
@@ -1,46 +1,87 @@
 # -*- coding: utf-8 -*-
 ##
 ## This file is part of Invenio.
 ## Copyright (C) 2014 CERN.
 ##
 ## Invenio 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 2 of the
 ## License, or (at your option) any later version.
 ##
 ## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
+"""
+    invenio.modules.uploader.workflows.insert
+    -----------------------------------------
+
+    Default workflows for insert records using the uploader.
+
+
+    :py:data:`insert`
+    :py:data:`undo`
+"""
+
 from workflow.patterns import IF
 
 from invenio.modules.uploader.errors import UploaderWorkflowException
-from invenio.modules.uploader.tasks import raise_, validate, update_pidstore,\
-    retrieve_record_id_from_pids, reserve_record_id, save_record, \
-    save_master_format
-
-insert = [
-    retrieve_record_id_from_pids(step=0),
-    IF(
-        lambda obj, eng: obj[1].get('recid') and
-        not eng.getVar('options').get('force', False),
-        [
-            raise_(UploaderWorkflowException(step=1,
-                msg="Record identifier found the input, you should use the"
-                    " option 'replace', 'correct' or 'append' mode instead.\n "
-                    "The option '--force' could also be used. (-h for help)"))
-         ]
-       ),
-    reserve_record_id(step=2),
-    validate(step=3),
-    save_record(step=4),
-    update_pidstore(step=5),
-    save_master_format(step=6),
-]
-
-undo = []
+from invenio.modules.uploader.uploader_tasks import raise_, \
+    validate, \
+    update_pidstore,\
+    retrieve_record_id_from_pids, \
+    reserve_record_id, save_record, \
+    save_master_format, \
+    create_records_for_workflow, \
+    return_recordids_only
+
+insert = dict(
+    pre_tasks=[
+        create_records_for_workflow,
+    ],
+    tasks=[
+        retrieve_record_id_from_pids(step=0),
+        IF(
+            lambda obj, eng: obj[1].get('recid') and
+            not eng.getVar('options').get('force', False),
+            [
+                raise_(UploaderWorkflowException(
+                    step=1,
+                    msg="Record identifier found the input, you should use the"
+                        " option 'replace', 'correct' or 'append' mode "
+                        "instead.\n The option '--force' could also be used. "
+                        "(-h for help)")
+                       )
+            ]
+        ),
+        reserve_record_id(step=2),
+        validate(step=3),
+        save_record(step=4),
+        update_pidstore(step=5),
+        save_master_format(step=6),
+    ],
+    post_tasks=[
+        return_recordids_only,
+    ]
+)
+"""
+Defaul insert workflow.
+
+TBC.
+"""
+
+undo = dict(
+    pre_tasks=[],
+    tasks=[],
+    post_tasks=[]
+)
+"""
+Default undo steps for the insert workflow.
+
+TBC.
+"""