diff --git a/fsl/utils/run.py b/fsl/utils/run.py
index f3a07b38fb483429842a73495d263517556e89b7..0f0fa0a080b8350d1a70e8e801eac01f5a93f8f7 100644
--- a/fsl/utils/run.py
+++ b/fsl/utils/run.py
@@ -11,18 +11,21 @@
 
    run
    runfsl
-   fslsub
+   wait
+   dryrun
 """
 
 
 import               logging
 import               contextlib
+import               collections
 import subprocess as sp
 import os.path    as op
 
 import               six
 
-from fsl.utils.platform import platform as fslplatform
+from   fsl.utils.platform import platform as fslplatform
+import fsl.utils.fslsub                   as fslsub
 
 
 log = logging.getLogger(__name__)
@@ -81,26 +84,52 @@ def run(*args, **kwargs):
     """Call a command and return its output. You can pass the command and
     arguments as a single string, or as a regular or unpacked sequence.
 
+    The command can be run on a cluster by using the ``submit`` keyword
+    argument.
+
     An exception is raised if the command returns a non-zero exit code, unless
-    the ``returnCode`` option is set to ``True``.
+    the ``ret`` option is set to ``True``.
+
+    :arg submit: Must be passed as a keyword argument. Defaults to ``None``.
+                 Accepted values are ``True`` or a
+                 If ``True``, the command is submitted as a cluster job via
+                 the :func:`.fslsub.submit` function.  May also be a
+                 dictionary containing arguments to that function.
+
+    :arg err:    Must be passed as a keyword argument. Defaults to
+                 ``False``. If ``True``, standard error is captured and
+                 returned. Ignored if ``submit`` is specified.
+
+    :arg ret:    Must be passed as a keyword argument. Defaults to ``False``.
+                 If ``True``, and the command's return code is non-0, an
+                 exception is not raised.  Ignored if ``submit`` is specified.
+
+    :returns:    If ``submit`` is provided, the cluster job ID is returned.
+                 Otherwise if ``err is False and ret is False`` (the default)
+                 a string containing the command's standard output.  is
+                 returned. Or, if ``err is True`` and/or ``ret is True``, a
+                 tuple containing the standard output, standard error (if
+                 ``err``), and return code (if ``ret``).
+    """
 
-    :arg err: Must be passed as a keyword argument. Defaults to
-              ``False``. If ``True``, standard error is captured and
-              returned.
+    err    = kwargs.get('err', False)
+    ret    = kwargs.get('ret', False)
+    submit = kwargs.get('ret', None)
+    args   = _prepareArgs(args)
 
-    :arg ret: Must be passed as a keyword argument. Defaults to
-              ``False``. If ``True``, and the command's return code
-              is non-0, an exception is not raised.
+    if not bool(submit):
+        submit = None
 
-    :returns: A string containing the command's standard output. Or,
-              if ``err is True`` and/or ``returnCode is True``, a tuple
-              containing the standard output, standard error (if
-              ``err``), and return code (if ``returnCode``).
-    """
+    if submit is not None:
+        err = False
+        ret = False
+
+        if submit is True:
+            submit = dict()
 
-    err  = kwargs.get('err', False)
-    ret  = kwargs.get('ret', False)
-    args = _prepareArgs(args)
+    if submit is not None and not isinstance(submit, collections.Mapping):
+        raise ValueError('submit must be a mapping containing '
+                         'options for fsl.utils.fslsub.submit')
 
     if DRY_RUN:
         log.debug('dryrun: {}'.format(' '.join(args)))
@@ -108,8 +137,15 @@ def run(*args, **kwargs):
         log.debug('run: {}'.format(' '.join(args)))
 
     if DRY_RUN:
-        stdout = ' '.join(args)
         stderr = ''
+        if submit is not None:
+            stdout = ' '.join(args)
+        else:
+            stdout = '[submit] ' + ' '.join(args)
+
+    elif submit is not None:
+        return fslsub.submit(' '.join(args), **submit)
+
     else:
         proc           = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE)
         stdout, stderr = proc.communicate()
@@ -148,6 +184,6 @@ def runfsl(*args, **kwargs):
     return run(*args, **kwargs)
 
 
-def fslsub(*args):
-    """Not implemented yet. """
-    raise NotImplementedError('')
+def wait(job_ids):
+    """Calls :func:`.fslsub.wait` for the given ``job_ids``. """
+    return fslsub.wait(job_ids)
diff --git a/fsl/wrappers/wrapperutils.py b/fsl/wrappers/wrapperutils.py
index 0a3ec94f4d78d01f694856a4f3cef032cf34e4b9..b1a4a18e839853254625b00711727104ee5e51c0 100644
--- a/fsl/wrappers/wrapperutils.py
+++ b/fsl/wrappers/wrapperutils.py
@@ -141,8 +141,9 @@ def cmdwrapper(func):
     :func:`fsl.utils.run.run` function in a standardised manner.
     """
     def wrapper(*args, **kwargs):
+        submit = kwargs.pop('submit', None)
         cmd = func(*args, **kwargs)
-        return run.run(cmd, err=True)
+        return run.run(cmd, err=True, submit=submit)
     return _update_wrapper(wrapper, func)
 
 
@@ -152,8 +153,9 @@ def fslwrapper(func):
     :func:`fsl.utils.run.runfsl` function in a standardised manner.
     """
     def wrapper(*args, **kwargs):
+        submit = kwargs.pop('submit', None)
         cmd = func(*args, **kwargs)
-        return run.runfsl(cmd, err=True)
+        return run.runfsl(cmd, err=True, submit=submit)
     return _update_wrapper(wrapper, func)