diff --git a/README.md b/README.md
index e16f6d5a4036b200c5b9078cd742c9fb0f69ec10..390f4081066964d84dd4b26be69ff00f3efcb025 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,35 @@
 
 - Docker: https://www.docker.com/
 - Python 3.5+
+
+## Setup
+
+In order to run jobs, Opaque needs access to a Docker swarm manager. Currently it's not possible to specify a dedicated Docker host, instead Opaque expects the executing system to to be a swarm manager.
+
+1. Get the source code and navigate into the code directory
+```
+git clone https://gitlab.ub.uni-bielefeld.de/sfb1288inf/opaque.git
+cd opaque
+```
+
+2. Create Docker swarm
+
+2.1. Local
+```
+# Set the variable values in setup_local_swarm.sh (nano setup_local_swarm.sh)
+./setup_local_swarm.sh
+```
+
+2.2. Distributed
+
+2.2.1. Initialize swarm on manager machine
+```
+docker swarm init
+```
+
+3. Create Python virtual environment, activate it and install the required python packages.
+```
+python3 -m venv venv
+source venv/bin/activate
+pip install -r requirements.txt
+```
diff --git a/app/__init__.py b/app/__init__.py
index 87043486c0d64f4b683b472d8201af79f78c393e..260f65bd43f2a31521d055b29d21f66d3b7b96b8 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -24,6 +24,9 @@ def create_app(config_name):
     scheduler.init_app(app)
     scheduler.start()
 
+    from .api import api as api_blueprint
+    app.register_blueprint(api_blueprint, url_prefix='/api')
+
     from .auth import auth as auth_blueprint
     app.register_blueprint(auth_blueprint, url_prefix='/auth')
 
diff --git a/app/api/__init__.py b/app/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..27a63156ed63721630000492f12d5c3df57178b5
--- /dev/null
+++ b/app/api/__init__.py
@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+api = Blueprint('api', __name__)
+
+from . import views
diff --git a/app/api/views.py b/app/api/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb86d1056017290aba9b0259371858c8fcbb89b9
--- /dev/null
+++ b/app/api/views.py
@@ -0,0 +1,76 @@
+from flask import abort, jsonify, request
+from flask_login import current_user, login_required
+from . import api
+from ..models import Job
+
+
+@api.route('/v1.0/corpora')
+@login_required
+def corpora():
+    corpora = []
+    for corpus in current_user.corpora.all():
+        corpora.append({'id': corpus.id,
+                        'creation_date': corpus.creation_date.timestamp(),
+                        'description': corpus.description,
+                        'title': corpus.title})
+    return jsonify(corpora)
+
+
+@api.route('/v1.0/corpora/<int:corpus_id>')
+@login_required
+def corpus(corpus_id):
+    corpus = current_user.corpora.filter_by(id=corpus_id).first()
+    if not corpus:
+        return abort(404)
+    return jsonify({'id': corpus.id,
+                    'creation_date': corpus.creation_date,
+                    'description': corpus.description,
+                    'title': corpus.title})
+
+
+@api.route('/v1.0/jobs')
+@login_required
+def jobs():
+    jobs = []
+    all = request.args.get('all')
+    if all and all.lower() == 'true':
+        if current_user.is_administrator():
+            jobs_query = Job.query
+        else:
+            return abort(403)
+    else:
+        jobs_query = current_user.jobs
+    for job in jobs_query.all():
+        jobs.append({'id': job.id,
+                     'creation_date': job.creation_date.timestamp(),
+                     'description': job.description,
+                     'end_date': (job.end_date.timestamp() if job.end_date else
+                                  None),
+                     'mem_mb': job.mem_mb,
+                     'n_cores': job.n_cores,
+                     'service': job.service,
+                     'service_args': job.service_args,
+                     'service_version': job.service_version,
+                     'status': job.status,
+                     'title': job.title})
+    return jsonify(jobs)
+
+
+@api.route('/v1.0/jobs/<int:job_id>')
+@login_required
+def job(job_id):
+    job = current_user.jobs.filter_by(id=job_id).first()
+    if not job:
+        return abort(404)
+    return jsonify({'id': job.id,
+                    'creation_date': job.creation_date.timestamp(),
+                    'description': job.description,
+                    'end_date': (job.end_date.timestamp() if job.end_date else
+                                 None),
+                    'mem_mb': job.mem_mb,
+                    'n_cores': job.n_cores,
+                    'service': job.service,
+                    'service_args': job.service_args,
+                    'service_version': job.service_version,
+                    'status': job.status,
+                    'title': job.title})