--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -55,13 +55,90 @@
     def from_python(self, value, state):
         return h.really_unicode(value or '')
 
+class TaskValidator(fev.FancyValidator):
+    def _to_python(self, value, state):
+        try:
+            mod, func = value.rsplit('.', 1)
+        except ValueError:
+            raise fe.Invalid('Invalid task name. Please provide the full '
+                    'dotted path to the python callable.', value, state)
+        try:
+            mod = __import__(mod, fromlist=[str(func)])
+        except ImportError:
+            raise fe.Invalid('Could not import "%s"' % value, value, state)
+
+        try:
+            task = getattr(mod, func)
+        except AttributeError:
+            raise fe.Invalid('Module has no attribute "%s"' % func, value, state)
+
+        if not hasattr(task, 'post'):
+            raise fe.Invalid('"%s" is not a task.' % value, value, state)
+        return task
+
+class UserValidator(fev.FancyValidator):
+    def _to_python(self, value, state):
+        from allura import model as M
+        user = M.User.by_username(value)
+        if not user:
+            raise fe.Invalid('Invalid username', value, state)
+        return user
+
+class PathValidator(fev.FancyValidator):
+    def _to_python(self, value, state):
+        from allura import model as M
+
+        parts = value.strip('/').split('/')
+        if len(parts) < 2:
+            raise fe.Invalid("You must specify at least a neighborhood and "
+                "project, i.e. '/nbhd/project'", value, state)
+        elif len(parts) == 2:
+            nbhd_name, project_name, app_name = parts[0], parts[1], None
+        elif len(parts) > 2:
+            nbhd_name, project_name, app_name = parts[0], parts[1], parts[2]
+
+        path_parts = {}
+        nbhd_url_prefix = '/%s/' % nbhd_name
+        nbhd = M.Neighborhood.query.get(url_prefix=nbhd_url_prefix)
+        if not nbhd:
+            raise fe.Invalid('Invalid neighborhood: %s' % nbhd_url_prefix, value, state)
+
+        project = M.Project.query.get(shortname=project_name, neighborhood_id=nbhd._id)
+        if not project:
+            raise fe.Invalid('Invalid project: %s' % project_name, value, state)
+
+        path_parts['project'] = project
+        if app_name:
+            app = project.app_instance(app_name)
+            if not app:
+                raise fe.Invalid('Invalid app mount point: %s' % app_name, value, state)
+            path_parts['app'] = app
+
+        return path_parts
+
 class JsonValidator(fev.FancyValidator):
+    """Validates a string as JSON and returns the original string"""
     def _to_python(self, value, state):
         try:
             json.loads(value)
         except ValueError, e:
             raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
         return value
+
+class JsonConverter(fev.FancyValidator):
+    """Deserializes a string to JSON and returns a Python object"""
+    def _to_python(self, value, state):
+        try:
+            obj = json.loads(value)
+        except ValueError, e:
+            raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
+        return obj
+
+class CreateTaskSchema(fe.Schema):
+    task = TaskValidator(not_empty=True, strip=True)
+    task_args = JsonConverter(if_missing=dict(args=[], kwargs={}))
+    user = UserValidator(strip=True, if_missing=None)
+    path = PathValidator(strip=True, if_missing={})
 
 class DateValidator(fev.FancyValidator):
     def _to_python(self, value, state):