diff --git a/.gitignore b/.gitignore
index 3c5d2e1e3fe18f48ed48dcce1bbc83845d06ce39..f20a5fa3f67c39e48da56a3b3f615ed8e96b7ec5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,4 @@
-__pycache__
-*.db
-*/env
 *.coverage
 */coverage
-htmlcov
-.pytest_cache
 /.idea
 .vs/
-/server/app/static/
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b02ef900e7a5db6b3da7e7593e38600b00b950bf..ffbd77a7309c46b530b1097e83ca3690b43fb5f9 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -41,5 +41,6 @@
   "search.exclude": {
     "**/env": true
   },
-  "python.pythonPath": "server\\env\\Scripts\\python.exe"
+  "python.pythonPath": "server\\env\\Scripts\\python.exe",
+  "restructuredtext.confPath": "${workspaceFolder}\\server\\sphinx\\source"
 }
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 7029dbe39d5a29ad684540e3d3eaf8272bb6bdaa..8b7286d134089b693c07ce77c748073701865719 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -23,7 +23,6 @@
         {
             "label": "Open client coverage",
             "type": "shell",
-            "group": "build",
             "command": "start ./output/coverage/jest/index.html",
             "problemMatcher": [],
             "options": {
@@ -54,7 +53,7 @@
             },
         },
         {
-            "label": "Populate server",
+            "label": "Populate database",
             "type": "shell",
             "group": "build",
             "command": "env/Scripts/python populate.py",
@@ -66,13 +65,30 @@
         {
             "label": "Open server coverage",
             "type": "shell",
-            "group": "build",
             "command": "start ./htmlcov/index.html",
             "problemMatcher": [],
             "options": {
                 "cwd": "${workspaceFolder}/server"
             },
         },
+        {
+            "label": "Generate server documentation",
+            "type": "shell",
+            "command": "../env/Scripts/activate; ./make html",
+            "problemMatcher": [],
+            "options": {
+                "cwd": "${workspaceFolder}/server/docs"
+            },
+        },
+        {
+            "label": "Open server documentation",
+            "type": "shell",
+            "command": "start index.html",
+            "problemMatcher": [],
+            "options": {
+                "cwd": "${workspaceFolder}/server/docs/build/html"
+            },
+        },
         {
             "label": "Start client and server",
             "group": "build",
diff --git a/server/.gitignore b/server/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6ff704b874b611b1add28bd5aebf2f79bdcdd87b
--- /dev/null
+++ b/server/.gitignore
@@ -0,0 +1,9 @@
+__pycache__
+env
+htmlcov
+.pytest_cache
+app/*.db
+app/static/
+
+# Documentation files
+docs/build
diff --git a/server/app/__init__.py b/server/app/__init__.py
index 2add65446ea6ca429a204a1564813d9fe19e25d0..4a28018cd5805ae6a99d70d7e749672b6f906eb2 100644
--- a/server/app/__init__.py
+++ b/server/app/__init__.py
@@ -7,6 +7,11 @@ from app.core.dto import MediaDTO
 
 
 def create_app(config_name="configmodule.DevelopmentConfig"):
+    """
+    Creates Flask app, returns it and a SocketIO instance. Call run on the
+    SocketIO instance and pass in the Flask app to start the server.
+    """
+
     app = Flask(__name__, static_url_path="/static", static_folder="static")
     app.config.from_object(config_name)
     app.url_map.strict_slashes = False
diff --git a/server/app/core/__init__.py b/server/app/core/__init__.py
index acfca55431cf0f9b538e1a1487cea7299e3ad4e4..94d1daf29b0e20e1f0bd241689b4128439d0c01d 100644
--- a/server/app/core/__init__.py
+++ b/server/app/core/__init__.py
@@ -1,3 +1,8 @@
+"""
+The core submodule contains everything important to the server that doesn't
+fit neatly in either apis or database.
+"""
+
 from app.database import Base, ExtendedQuery
 from flask_bcrypt import Bcrypt
 from flask_jwt_extended.jwt_manager import JWTManager
diff --git a/server/app/core/codes.py b/server/app/core/codes.py
index 47150767b17fb1b352f0b1ae3130dc6d4ffefb5a..ad6d844c0fa4a01cf7652a1c966b25f678f73a4e 100644
--- a/server/app/core/codes.py
+++ b/server/app/core/codes.py
@@ -1,3 +1,7 @@
+"""
+Contains all functions purely related to creating and verifying a code.
+"""
+
 import random
 import re
 import string
@@ -7,9 +11,14 @@ ALLOWED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 CODE_RE = re.compile(f"^[{ALLOWED_CHARS}]{{{CODE_LENGTH}}}$")
 
 
-def generate_code_string():
+def generate_code_string() -> str:
+    """Generates a 6 character long random sequence containg uppercase letters
+    and numbers.
+    """
     return "".join(random.choices(ALLOWED_CHARS, k=CODE_LENGTH))
 
 
-def verify_code(c):
-    return CODE_RE.search(c.upper()) is not None
+def verify_code(code: str) -> bool:
+    """Returns True if code only contains letters and/or numbers
+    and is exactly 6 characters long."""
+    return CODE_RE.search(code.upper()) is not None
diff --git a/server/app/core/dto.py b/server/app/core/dto.py
index 90e49d00b8062ee3afe6eac7b44e7b397ccfe45c..6bbbdf621f52855bf6565fa162007cd3956bbd85 100644
--- a/server/app/core/dto.py
+++ b/server/app/core/dto.py
@@ -1,3 +1,8 @@
+"""
+The DTO module (short for Data Transfer Object) connects the namespace of an
+API and its related schemas.
+"""
+
 import app.core.rich_schemas as rich_schemas
 import app.core.schemas as schemas
 from flask_restx import Namespace
diff --git a/server/app/core/files.py b/server/app/core/files.py
index 01a03166811b02f06e2b21f21430dd25837b2067..5d22c8e0ad8672a6db56e7ac4c64087b55a26b74 100644
--- a/server/app/core/files.py
+++ b/server/app/core/files.py
@@ -1,30 +1,35 @@
+"""
+Contains functions related to file handling, mainly saving and deleting images.
+"""
+
 from PIL import Image, ImageChops
-from flask import current_app
+from flask import current_app, has_app_context
 import os
 import datetime
 from flask_uploads import IMAGES, UploadSet
 
-PHOTO_PATH = current_app.config["UPLOADED_PHOTOS_DEST"]
-THUMBNAIL_SIZE = current_app.config["THUMBNAIL_SIZE"]
-image_set = UploadSet("photos", IMAGES)
+if has_app_context():
+    PHOTO_PATH = current_app.config["UPLOADED_PHOTOS_DEST"]
+    THUMBNAIL_SIZE = current_app.config["THUMBNAIL_SIZE"]
+    image_set = UploadSet("photos", IMAGES)
 
 
-def compare_images(input_image, output_image):
-    # compare image dimensions (assumption 1)
-    if input_image.size != output_image.size:
-        return False
+# def compare_images(input_image, output_image):
+#     # compare image dimensions (assumption 1)
+#     if input_image.size != output_image.size:
+#         return False
 
-    rows, cols = input_image.size
+#     rows, cols = input_image.size
 
-    # compare image pixels (assumption 2 and 3)
-    for row in range(rows):
-        for col in range(cols):
-            input_pixel = input_image.getpixel((row, col))
-            output_pixel = output_image.getpixel((row, col))
-            if input_pixel != output_pixel:
-                return False
+#     # compare image pixels (assumption 2 and 3)
+#     for row in range(rows):
+#         for col in range(cols):
+#             input_pixel = input_image.getpixel((row, col))
+#             output_pixel = output_image.getpixel((row, col))
+#             if input_pixel != output_pixel:
+#                 return False
 
-    return True
+#     return True
 
 
 def _delete_image(filename):
@@ -33,6 +38,10 @@ def _delete_image(filename):
 
 
 def save_image_with_thumbnail(image_file):
+    """
+    Saves the given image and also creates a small thumbnail for it.
+    """
+
     saved_filename = image_set.save(image_file)
     saved_path = os.path.join(PHOTO_PATH, saved_filename)
     with Image.open(saved_path) as im:
@@ -45,6 +54,9 @@ def save_image_with_thumbnail(image_file):
 
 
 def delete_image_and_thumbnail(filename):
+    """
+    Delete the given image together with its thumbnail.
+    """
     _delete_image(filename)
     _delete_image(f"thumbnail_{filename}")
 
diff --git a/server/app/core/http_codes.py b/server/app/core/http_codes.py
index 95a99175665427e51b8684792e98180c75e6dc72..f6f19ed13519be9ff5b76ca2c6c53e3cfb91c3c2 100644
--- a/server/app/core/http_codes.py
+++ b/server/app/core/http_codes.py
@@ -1,3 +1,7 @@
+"""
+This module defines all the http status codes thats used in the api.
+"""
+
 OK = 200
 NO_CONTENT = 204
 BAD_REQUEST = 400
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index 160d67a0af7b58483202c00c1ec6b97097de0633..4c1525207990398475fd0c88aec11c56f87eaff1 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -1,3 +1,7 @@
+"""
+This module contains the parsers used to parse the data gotten in api requests.
+"""
+
 from flask_restx import inputs, reqparse
 
 
diff --git a/server/app/core/rich_schemas.py b/server/app/core/rich_schemas.py
index ae1c6ab6e0bbd63ebd1369e1741672a6489c87fa..7d883584776677ee1be21975ed5ae413dc2019fa 100644
--- a/server/app/core/rich_schemas.py
+++ b/server/app/core/rich_schemas.py
@@ -1,3 +1,8 @@
+"""
+This module contains rich schemas used to convert database objects into 
+dictionaries. This is the rich variant which means that objects will 
+pull in other whole objects instead of just the id.
+"""
 import app.core.schemas as schemas
 import app.database.models as models
 from app.core import ma
diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py
index 4008b8869cc084022d4d46386d89ec29ca1765d2..73ac21cbd396d1ee175e6151003d05f1a10dc82b 100644
--- a/server/app/core/schemas.py
+++ b/server/app/core/schemas.py
@@ -1,3 +1,8 @@
+"""
+This module contains schemas used to convert database objects into 
+dictionaries.
+"""
+
 from marshmallow.decorators import pre_load
 from marshmallow.decorators import pre_dump, post_dump
 import app.database.models as models
diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py
index b62f82c9365818ac76faa0cd05e09bc795d0c071..78c88241f8fbd920168320d4e7d51f5ae15cf160 100644
--- a/server/app/core/sockets.py
+++ b/server/app/core/sockets.py
@@ -1,3 +1,10 @@
+"""
+Contains all functionality related sockets. That is starting and ending a presentation, 
+joining and leaving a presentation and syncing slides and timer bewteen all clients
+connected to the same presentation.
+"""
+
+from typing import Dict
 import app.database.controller as dbc
 from app.core import db
 from app.database.models import Competition, Slide, Team, ViewType, Code
@@ -20,12 +27,16 @@ presentations = {}
 
 
 @sio.on("connect")
-def connect():
+def connect() -> None:
     logger.info(f"Client '{request.sid}' connected")
 
 
 @sio.on("disconnect")
-def disconnect():
+def disconnect() -> None:
+    """
+    Remove client from the presentation it was in. Delete presentation if no
+    clients are connected to it.
+    """
     for competition_id, presentation in presentations.items():
         if request.sid in presentation["clients"]:
             del presentation["clients"][request.sid]
@@ -40,7 +51,11 @@ def disconnect():
 
 
 @sio.on("start_presentation")
-def start_presentation(data):
+def start_presentation(data: Dict) -> None:
+    """
+    Starts a presentation if that competition is currently not active.
+    """
+
     competition_id = data["competition_id"]
 
     if competition_id in presentations:
@@ -62,7 +77,15 @@ def start_presentation(data):
 
 
 @sio.on("end_presentation")
-def end_presentation(data):
+def end_presentation(data: Dict) -> None:
+    """
+    End a presentation by sending end_presentation to all connected clients.
+
+    The only clients allowed to do this is the one that started the presentation.
+
+    Log error message if no presentation exists with the send id or if this
+    client is not in that presentation.
+    """
     competition_id = data["competition_id"]
 
     if competition_id not in presentations:
@@ -91,8 +114,13 @@ def end_presentation(data):
 
 
 @sio.on("join_presentation")
-def join_presentation(data):
-    team_view_id = 1
+def join_presentation(data: Dict) -> None:
+    """
+    Join a currently active presentation.
+
+    Log error message if given code doesn't exist, if not presentation associated
+    with that code exists or if client is already in the presentation.
+    """
     code = data["code"]
     item_code = db.session.query(Code).filter(Code.code == code).first()
 
@@ -126,7 +154,15 @@ def join_presentation(data):
 
 
 @sio.on("set_slide")
-def set_slide(data):
+def set_slide(data: Dict) -> None:
+    """
+    Sync slides between all clients in the same presentation by sending
+    set_slide to them.
+
+    Log error if the given competition_id is not active, if client is not in
+    that presentation or the client is not the one who started the presentation.
+    """
+
     competition_id = data["competition_id"]
     slide_order = data["slide_order"]
 
@@ -165,7 +201,14 @@ def set_slide(data):
 
 
 @sio.on("set_timer")
-def set_timer(data):
+def set_timer(data: Dict) -> None:
+    """
+    Sync slides between all clients in the same presentation by sending
+    set_timer to them.
+
+    Log error if the given competition_id is not active, if client is not in
+    that presentation or the client is not the one who started the presentation.
+    """
     competition_id = data["competition_id"]
     timer = data["timer"]
 
diff --git a/server/app/database/__init__.py b/server/app/database/__init__.py
index e0c78ad3af3f3376fdecdae4eae219aa1c62be24..9b00283080b17329b83c93fd23e4dcfa3485f9ae 100644
--- a/server/app/database/__init__.py
+++ b/server/app/database/__init__.py
@@ -1,3 +1,8 @@
+"""
+The database submodule contaisn all functionality that has to do with the
+database. It can add, get, delete, edit, search and copy items.
+"""
+
 import json
 
 from flask_restx import abort
@@ -16,7 +21,15 @@ class Base(Model):
 
 
 class ExtendedQuery(BaseQuery):
+    """
+    Extensions to a regular query which makes using the database more convenient.
+    """
+
     def first_extended(self, required=True, error_message=None, error_code=404):
+        """
+        Extensions of the first() functions otherwise used on queries. Abort
+        if no item was found and it was required.
+        """
         item = self.first()
 
         if required and not item:
@@ -27,6 +40,10 @@ class ExtendedQuery(BaseQuery):
         return item
 
     def pagination(self, page=0, page_size=15, order_column=None, order=1):
+        """
+        When looking for lists of items this is used to only return a few of
+        them to allow for pagination.
+        """
         query = self
         if order_column:
             if order == 1:
@@ -40,17 +57,17 @@ class ExtendedQuery(BaseQuery):
         return items, total
 
 
-class Dictionary(TypeDecorator):
+# class Dictionary(TypeDecorator):
 
-    impl = Text
+#     impl = Text
 
-    def process_bind_param(self, value, dialect):
-        if value is not None:
-            value = json.dumps(value)
+#     def process_bind_param(self, value, dialect):
+#         if value is not None:
+#             value = json.dumps(value)
 
-        return value
+#         return value
 
-    def process_result_value(self, value, dialect):
-        if value is not None:
-            value = json.loads(value)
-        return value
+#     def process_result_value(self, value, dialect):
+#         if value is not None:
+#             value = json.loads(value)
+#         return value
diff --git a/server/app/database/controller/__init__.py b/server/app/database/controller/__init__.py
index a46a65f1e6c2fe4e0664c462288a5f00c1cdd46c..b8d6fca38a446d447607362a871c437bfa713bfe 100644
--- a/server/app/database/controller/__init__.py
+++ b/server/app/database/controller/__init__.py
@@ -1,3 +1,7 @@
-# import add, get
+"""
+The controller subpackage provides a simple interface to the database. It 
+exposes methods to simply add, copy, delete, edit, get and search for items.
+"""
+
 from app.core import db
 from app.database.controller import add, copy, delete, edit, get, search, utils
diff --git a/server/app/database/models.py b/server/app/database/models.py
index f9f18438ad07edf951cebd24ad7ab92e9b854299..d4e177b070755d75cada2988e43468f28794146b 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -1,3 +1,9 @@
+"""
+This file contains every model in the database. In regular SQL terms, it 
+defines every table, the fields in those tables and their relationship to 
+each other.
+"""
+
 from app.core import bcrypt, db
 from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
 
diff --git a/server/app/database/types.py b/server/app/database/types.py
index f53835eccf10599eb9ab81d8af1426e02a21e405..46db168cdbf9e1dce2727bacefac7fd823a9de39 100644
--- a/server/app/database/types.py
+++ b/server/app/database/types.py
@@ -1,3 +1,7 @@
+"""
+This module defines the different component types.
+"""
+
 ID_TEXT_COMPONENT = 1
 ID_IMAGE_COMPONENT = 2
 ID_QUESTION_COMPONENT = 3
\ No newline at end of file
diff --git a/server/docs/Makefile b/server/docs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d0c3cbf1020d5c292abdedf27627c6abe25e2293
--- /dev/null
+++ b/server/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/server/docs/make.bat b/server/docs/make.bat
new file mode 100644
index 0000000000000000000000000000000000000000..9534b018135ed7d5caed6298980c55e8b1d2ec82
--- /dev/null
+++ b/server/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/server/docs/source/conf.py b/server/docs/source/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..e42217c3e3b4b40f15683e02e0d544176901e033
--- /dev/null
+++ b/server/docs/source/conf.py
@@ -0,0 +1,66 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+
+import os
+import sys
+
+basepath = os.path.dirname(__file__)
+filepath = os.path.abspath(os.path.join(basepath, "../.."))
+sys.path.insert(0, filepath)
+
+
+# -- Project information -----------------------------------------------------
+
+project = "Teknikattan scoring system"
+copyright = "2021, Albin Henriksson, Sebastian Karlsson, Victor Löfgren, Björn Modée, Josef Olsson, Max Rüdinger, Carl Schönfelder, Emil Wahlqvist"
+author = "Albin Henriksson, Sebastian Karlsson, Victor Löfgren, Björn Modée, Josef Olsson, Max Rüdinger, Carl Schönfelder, Emil Wahlqvist"
+version = "1.0"
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ["sphinx.ext.autodoc", "myst_parser"]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+autodoc_member_order = "bysource"
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = "alabaster"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+# html_static_path = ["_static"]
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+add_module_names = False
+
+
+logo_path = os.path.abspath(os.path.join(basepath, "../../app/static/images/t8.jpg"))
+html_logo = logo_path
+
+# favicon_path = os.path.abspath(os.path.join(basepath, "../../../client/public/favicon.ico"))
+# html_favicon = favicon_path
diff --git a/server/docs/source/development.md b/server/docs/source/development.md
new file mode 100644
index 0000000000000000000000000000000000000000..a6b07cc6664fa20e4d6dad57f12cdb19eb2e982e
--- /dev/null
+++ b/server/docs/source/development.md
@@ -0,0 +1,59 @@
+# Development
+
+In this section we give all the instructions necessary to continue the development of this project.
+We also give some recommentations for how to go about it and some ides for how to improve it.
+
+## Working with Python
+
+In this section we briefly describe how to work with Python.
+
+### Virtual environments
+
+Python virtual environments are used to isolate packages for each project from each other.
+In the [Installation](installation.md) you installed `virtualenv` and created and activated a virtual environment.
+
+### Pip
+
+Python uses `pip` to manage it's packages.
+Here we briefly describe to use it.
+All of the following instructions assume you have created and activated a virtual environment.
+
+To install a package, run `pip install <package>`.
+
+To uninstall a package, run `pip uninstall <package>`.
+
+To save a package as a dependency to the project, run `pip freeze > requirements.txt`.
+
+To install all project dependencies, run `pip install -r requirements.txt`.
+
+## Visual Studio Code
+
+The development of this project was mainly done using Visual Studio Code (VSCode).
+It is not that surprising, then, that we recommend you use it.
+
+### Tasks
+
+A task in VSCode is a simple action that can be run by pressing `ctrl+shift+p` and selecting `Tasks: Run Task`.
+A few such tasks has been setup in this project and tasks related to the server will be described below.
+
+The `Start server` task will start the server.
+
+The `Populate database` task will populate the database with a few competitions, teams, users and such.
+
+The `Test server` task will run the server tests located in the `tests/` folder.
+
+The `Open server coverage` can only be run after running the server tests and will open the coverage report generated by those tests in a webbrowser.
+
+The `Generate server documentation` will generate the server documentation, i.e. this document, in the `docs/build/html/` folder.
+
+The `Open server documentation` can only be run after generating the documentation and will open it in a webbrowser.
+
+## Further development
+
+Because the project was time limited a lot is left to be done.
+A few ideas for things to be improved are given here.
+
+### Replacing reqparse
+
+As mention in the [Parsing request](overview.md#Parsing-request), the reqparse module from RestX is deprecated and should be replaced with for example marsmallow.
+Parsing is a rather small and simple matter which makes it quite fine not to use the most optimal tool, but it should nevertheless be replaced.
diff --git a/server/docs/source/index.rst b/server/docs/source/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..df8af7fb5c63ad522026b637ee660f784428d07d
--- /dev/null
+++ b/server/docs/source/index.rst
@@ -0,0 +1,33 @@
+Welcome to Teknikattan scoring system's documentation!
+======================================================
+
+This is the documentation for the backend of teknikattans scorings system.
+Below you will find an overview of how the backend works.
+Then you will find instructions for how to install it and get it up and running.
+You will then find instructions for how to continue the development of this project.
+Last you will find documentation on all of the modules.
+
+Documentation
+=============
+
+.. toctree::
+   :maxdepth: 2
+
+   overview
+   installation
+   development
+
+Modules
+=======
+
+.. toctree::
+   :maxdepth: 1
+
+   modules/app
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/server/docs/source/installation.md b/server/docs/source/installation.md
new file mode 100644
index 0000000000000000000000000000000000000000..700ec0c44355dd93e548ac0db1edc69ab2a040b0
--- /dev/null
+++ b/server/docs/source/installation.md
@@ -0,0 +1,45 @@
+# Installation
+
+It is recommended to use [Visual Studio Code](https://code.visualstudio.com/) to install and use the server, but it is not necessary.
+In order to install the server, you will need to do the following:
+
+Install [Python](https://www.python.org/downloads/).
+
+Clone [teknikattan-scoring-system](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system).
+
+Open a terminal and navigate to the root of the cloned project.
+
+Install virtualenv and create a virtual environment:
+
+```
+pip install virtualenv
+cd server
+py -m venv env
+```
+
+Activate the virtual environment (which is done slightly differently on Windows and Linux/Mac):
+
+On Windows:
+
+```
+Set-ExecutionPolicy Unrestricted -Scope Process
+./env/Scripts/activate
+```
+
+On Linux/Mac:
+
+```
+source env/bin/activate
+```
+
+Install all project depencies:
+
+```
+pip install -r requirements.txt
+```
+
+You should now be ready to start the server.
+Try it by running `python main.py` and navigate to `localhost:5000`.
+If everything worked as it should you should see a list of all available API calls.
+
+Continue to [Development](development.md).
diff --git a/server/docs/source/modules/app.apis.alternatives.rst b/server/docs/source/modules/app.apis.alternatives.rst
new file mode 100644
index 0000000000000000000000000000000000000000..dc712f7cdc7172925b608b5390441abfe2a7b7f2
--- /dev/null
+++ b/server/docs/source/modules/app.apis.alternatives.rst
@@ -0,0 +1,7 @@
+app.apis.alternatives module
+============================
+
+.. automodule:: app.apis.alternatives
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.answers.rst b/server/docs/source/modules/app.apis.answers.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f59bdc1ff5dee23672baa13f5f0b1003f589dac7
--- /dev/null
+++ b/server/docs/source/modules/app.apis.answers.rst
@@ -0,0 +1,7 @@
+app.apis.answers module
+=======================
+
+.. automodule:: app.apis.answers
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.auth.rst b/server/docs/source/modules/app.apis.auth.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7da9d2ea0c686420050bd1d7b4dc5ca25a08f80a
--- /dev/null
+++ b/server/docs/source/modules/app.apis.auth.rst
@@ -0,0 +1,7 @@
+app.apis.auth module
+====================
+
+.. automodule:: app.apis.auth
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.codes.rst b/server/docs/source/modules/app.apis.codes.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3ee856a509714af289986ac6035d3486009c47c6
--- /dev/null
+++ b/server/docs/source/modules/app.apis.codes.rst
@@ -0,0 +1,7 @@
+app.apis.codes module
+=====================
+
+.. automodule:: app.apis.codes
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.competitions.rst b/server/docs/source/modules/app.apis.competitions.rst
new file mode 100644
index 0000000000000000000000000000000000000000..305d952e6d61ad9f480361adfdb26a1ca235a69b
--- /dev/null
+++ b/server/docs/source/modules/app.apis.competitions.rst
@@ -0,0 +1,7 @@
+app.apis.competitions module
+============================
+
+.. automodule:: app.apis.competitions
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.components.rst b/server/docs/source/modules/app.apis.components.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1db7934e9c1d6afb41fb765cf41d9e8aef4ec881
--- /dev/null
+++ b/server/docs/source/modules/app.apis.components.rst
@@ -0,0 +1,7 @@
+app.apis.components module
+==========================
+
+.. automodule:: app.apis.components
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.media.rst b/server/docs/source/modules/app.apis.media.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f82097d139f9b9804002b1c7c20a018f47e33e39
--- /dev/null
+++ b/server/docs/source/modules/app.apis.media.rst
@@ -0,0 +1,7 @@
+app.apis.media module
+=====================
+
+.. automodule:: app.apis.media
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.misc.rst b/server/docs/source/modules/app.apis.misc.rst
new file mode 100644
index 0000000000000000000000000000000000000000..43dc154aa1d6e42cf6e5a76b843029b19b0f96d0
--- /dev/null
+++ b/server/docs/source/modules/app.apis.misc.rst
@@ -0,0 +1,7 @@
+app.apis.misc module
+====================
+
+.. automodule:: app.apis.misc
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.questions.rst b/server/docs/source/modules/app.apis.questions.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ffab7b63eeb1dd68c454ff02b94fe0f4b3bcd8e9
--- /dev/null
+++ b/server/docs/source/modules/app.apis.questions.rst
@@ -0,0 +1,7 @@
+app.apis.questions module
+=========================
+
+.. automodule:: app.apis.questions
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.rst b/server/docs/source/modules/app.apis.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1f372a16b941e7a2b0555a6688078adadf182243
--- /dev/null
+++ b/server/docs/source/modules/app.apis.rst
@@ -0,0 +1,26 @@
+api
+===
+
+.. automodule:: app.apis
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   app.apis.alternatives
+   app.apis.answers
+   app.apis.auth
+   app.apis.codes
+   app.apis.competitions
+   app.apis.components
+   app.apis.media
+   app.apis.misc
+   app.apis.questions
+   app.apis.slides
+   app.apis.teams
+   app.apis.users
diff --git a/server/docs/source/modules/app.apis.slides.rst b/server/docs/source/modules/app.apis.slides.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7525025ca8c4ffe44bdc9338cc8209cafcb8548c
--- /dev/null
+++ b/server/docs/source/modules/app.apis.slides.rst
@@ -0,0 +1,7 @@
+app.apis.slides module
+======================
+
+.. automodule:: app.apis.slides
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.teams.rst b/server/docs/source/modules/app.apis.teams.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0abf49f30aeebdc457b3985bdf38df9adc9f2069
--- /dev/null
+++ b/server/docs/source/modules/app.apis.teams.rst
@@ -0,0 +1,7 @@
+app.apis.teams module
+=====================
+
+.. automodule:: app.apis.teams
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.apis.users.rst b/server/docs/source/modules/app.apis.users.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a40fc2f4f1157f0611d7911df468d53461bc2149
--- /dev/null
+++ b/server/docs/source/modules/app.apis.users.rst
@@ -0,0 +1,7 @@
+app.apis.users module
+=====================
+
+.. automodule:: app.apis.users
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.core.codes.rst b/server/docs/source/modules/app.core.codes.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0e6f5a6cdc59c3693170602d68b09823f3bc7900
--- /dev/null
+++ b/server/docs/source/modules/app.core.codes.rst
@@ -0,0 +1,7 @@
+codes
+=====
+
+.. automodule:: app.core.codes
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.core.dto.rst b/server/docs/source/modules/app.core.dto.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bc77b7beddcc362e0d1e28aeba72a9a499e7359f
--- /dev/null
+++ b/server/docs/source/modules/app.core.dto.rst
@@ -0,0 +1,5 @@
+dto
+===
+
+.. automodule:: app.core.dto
+   :members:
diff --git a/server/docs/source/modules/app.core.files.rst b/server/docs/source/modules/app.core.files.rst
new file mode 100644
index 0000000000000000000000000000000000000000..38808edcd448ff05da2de861c814494714a7ccdf
--- /dev/null
+++ b/server/docs/source/modules/app.core.files.rst
@@ -0,0 +1,7 @@
+files
+=====
+
+.. automodule:: app.core.files
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.core.http_codes.rst b/server/docs/source/modules/app.core.http_codes.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ee3c1d25f3764a96b8b20f08db38c728ac457fe5
--- /dev/null
+++ b/server/docs/source/modules/app.core.http_codes.rst
@@ -0,0 +1,5 @@
+http_codes
+==========
+
+.. automodule:: app.core.http_codes
+
diff --git a/server/docs/source/modules/app.core.parsers.rst b/server/docs/source/modules/app.core.parsers.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9875cb6b9953c48075caa9376642dfd083849bba
--- /dev/null
+++ b/server/docs/source/modules/app.core.parsers.rst
@@ -0,0 +1,7 @@
+parsers
+=======
+
+.. automodule:: app.core.parsers
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.core.rich_schemas.rst b/server/docs/source/modules/app.core.rich_schemas.rst
new file mode 100644
index 0000000000000000000000000000000000000000..38c7283be6a399f903918f1ab28a320b335cb21a
--- /dev/null
+++ b/server/docs/source/modules/app.core.rich_schemas.rst
@@ -0,0 +1,4 @@
+rich schemas
+============
+
+.. automodule:: app.core.rich_schemas
diff --git a/server/docs/source/modules/app.core.rst b/server/docs/source/modules/app.core.rst
new file mode 100644
index 0000000000000000000000000000000000000000..4452b4638b3c0e101f1d4e5b2e477bb8fc5c8332
--- /dev/null
+++ b/server/docs/source/modules/app.core.rst
@@ -0,0 +1,22 @@
+core
+====
+
+.. automodule:: app.core
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   app.core.codes
+   app.core.dto
+   app.core.files
+   app.core.http_codes
+   app.core.parsers
+   app.core.rich_schemas
+   app.core.schemas
+   app.core.sockets
diff --git a/server/docs/source/modules/app.core.schemas.rst b/server/docs/source/modules/app.core.schemas.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b2b864c6ab93e192fe20e97021a0461a3bc74d46
--- /dev/null
+++ b/server/docs/source/modules/app.core.schemas.rst
@@ -0,0 +1,4 @@
+schemas
+=======
+
+.. automodule:: app.core.schemas
diff --git a/server/docs/source/modules/app.core.sockets.rst b/server/docs/source/modules/app.core.sockets.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c331255b30adc2463650b04912c0987aea1cd5c8
--- /dev/null
+++ b/server/docs/source/modules/app.core.sockets.rst
@@ -0,0 +1,7 @@
+sockets
+=======
+
+.. automodule:: app.core.sockets
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.add.rst b/server/docs/source/modules/app.database.controller.add.rst
new file mode 100644
index 0000000000000000000000000000000000000000..72163b22751e1b28e2db44a9900af4a95ff7de9a
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.add.rst
@@ -0,0 +1,7 @@
+app.database.controller.add module
+==================================
+
+.. automodule:: app.database.controller.add
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.copy.rst b/server/docs/source/modules/app.database.controller.copy.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6a8cbde8d4313aee7c43c7ff2c381787a8a642dc
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.copy.rst
@@ -0,0 +1,7 @@
+app.database.controller.copy module
+===================================
+
+.. automodule:: app.database.controller.copy
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.delete.rst b/server/docs/source/modules/app.database.controller.delete.rst
new file mode 100644
index 0000000000000000000000000000000000000000..cd3c7572d62af3e20e6eca38729333857f810969
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.delete.rst
@@ -0,0 +1,7 @@
+app.database.controller.delete module
+=====================================
+
+.. automodule:: app.database.controller.delete
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.edit.rst b/server/docs/source/modules/app.database.controller.edit.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b81d5a8af83b4c3e8c28aee3b9d257eb4de29f8c
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.edit.rst
@@ -0,0 +1,7 @@
+app.database.controller.edit module
+===================================
+
+.. automodule:: app.database.controller.edit
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.get.rst b/server/docs/source/modules/app.database.controller.get.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d02168e9c2222cf11e7b7b64e0b4b7453d5e6576
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.get.rst
@@ -0,0 +1,7 @@
+app.database.controller.get module
+==================================
+
+.. automodule:: app.database.controller.get
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.rst b/server/docs/source/modules/app.database.controller.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c1dcf93778de413ec822ff15ba6bc6e7aa4f5267
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.rst
@@ -0,0 +1,21 @@
+app.database.controller package
+===============================
+
+.. automodule:: app.database.controller
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   app.database.controller.add
+   app.database.controller.copy
+   app.database.controller.delete
+   app.database.controller.edit
+   app.database.controller.get
+   app.database.controller.search
+   app.database.controller.utils
diff --git a/server/docs/source/modules/app.database.controller.search.rst b/server/docs/source/modules/app.database.controller.search.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6c1052c3f08d05e25d4bdca69a3695424b8bd14a
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.search.rst
@@ -0,0 +1,7 @@
+app.database.controller.search module
+=====================================
+
+.. automodule:: app.database.controller.search
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.controller.utils.rst b/server/docs/source/modules/app.database.controller.utils.rst
new file mode 100644
index 0000000000000000000000000000000000000000..997cfeac6d4c942ea62789e7636dda9c74fb8ca5
--- /dev/null
+++ b/server/docs/source/modules/app.database.controller.utils.rst
@@ -0,0 +1,7 @@
+app.database.controller.utils module
+====================================
+
+.. automodule:: app.database.controller.utils
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.models.rst b/server/docs/source/modules/app.database.models.rst
new file mode 100644
index 0000000000000000000000000000000000000000..633beb8fd64a5c17a13dfaf27610fd98ea292a5f
--- /dev/null
+++ b/server/docs/source/modules/app.database.models.rst
@@ -0,0 +1,7 @@
+app.database.models module
+==========================
+
+.. automodule:: app.database.models
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.database.rst b/server/docs/source/modules/app.database.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c6f25650033ab9992d9547e787058cb841339b64
--- /dev/null
+++ b/server/docs/source/modules/app.database.rst
@@ -0,0 +1,24 @@
+app.database package
+====================
+
+.. automodule:: app.database
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   app.database.controller
+
+Submodules
+----------
+
+.. toctree::
+   :maxdepth: 4
+
+   app.database.models
+   app.database.types
diff --git a/server/docs/source/modules/app.database.types.rst b/server/docs/source/modules/app.database.types.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e33a58579a26037bc9c091abc1c24e2fc489164a
--- /dev/null
+++ b/server/docs/source/modules/app.database.types.rst
@@ -0,0 +1,7 @@
+app.database.types module
+=========================
+
+.. automodule:: app.database.types
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/server/docs/source/modules/app.rst b/server/docs/source/modules/app.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e471b989647515781c7710acc4e830026c8db24e
--- /dev/null
+++ b/server/docs/source/modules/app.rst
@@ -0,0 +1,17 @@
+app
+===
+
+.. automodule:: app
+   :members: create_app
+   :undoc-members:
+   :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 1
+
+   app.apis
+   app.core
+   app.database
diff --git a/server/docs/source/overview.md b/server/docs/source/overview.md
new file mode 100644
index 0000000000000000000000000000000000000000..67645bf68718d8c074be28c3858a35175c069d41
--- /dev/null
+++ b/server/docs/source/overview.md
@@ -0,0 +1,81 @@
+# Server
+
+The backend is mainly responsible for storing every user and all competitions.
+It also needs to make sure the every API call and socket event is authorized.
+The server is written in Python together with the micro-framework Flask.
+
+## Overview
+
+The server has two main responsibilites.
+The first is to handle API calls from the client to store, update and delete information, such as competitions or users.
+It also needs to make sure that only authorized people can access these.
+The other is to sync slides, timer and answers between clients in an active competition.
+Both of these will be described in more detail below.
+
+## Receiving API calls
+
+An API call is a way the client can communicates with the server.
+When a request is received the server begins by authorizing it (making sure the person sending the request is allowed to access the route).
+After that it makes sure that it got all information in the request it needed.
+The server will then do the thing the client requested.
+And finally it will need to generate repsonse, usually in the form of an object from the database.
+All of these steps are described in more detail below.
+
+### Routes
+
+Each route which is possible to call is specified in the files in the `app/apis/` folder.
+All available routes can also be seen by navigating to `localhost:5000` after starting the server.
+
+### Authorization
+
+When the server receives an API call the first thing it does is to authorize it.
+The authorization is done using JSON Web Tokens (JWT) by comparing the contents of them with what is expected.
+Whenever a client logs into an account or joins a competition, it is given a JWT generated by the server, and the client will need to use this token in every subsequent request sent to the server to authenticate itself.
+
+What authorization to be done on the server is specified by the `@protect_route()` decorator.
+This decorator specifies who is allowed to access this route, which can either be users with specific roles, or people who have joined competitions with specific views.
+If the route is not decorated everyone is allowed to access it, the only routes currently like that is logging in as a user and joining a competition, by necessity.
+
+### Parsing request
+
+After the request is authorized the server will need to parse contents of the request.
+The parsing is done with [reqparse](https://flask-restx.readthedocs.io/en/latest/parsing.html) from RestX (this module is deprecated and should be replaced).
+Each API call expects different parameters in different places and this is specificied in each of the files in `app/apis/` folder, together with the route.
+
+### Handling request
+
+After the request has been authorized and parsed the server needs to act on the request.
+What the server does of course depends on the route and given arguments, but it usually gets, edits or deletes something from the database.
+The server uses an SQL database and interfaces to it via SQLAlchemy.
+Everything related to the database is located in the `app/database/` folder.
+
+### Responding
+
+When the server is done handling the request it usually responds with an item from the database.
+Converting a database object to json is done with [Marsmallow](https://marshmallow.readthedocs.io/en/stable/).
+How to do this conversion is specified in two files in in the folder `app/core/`.
+The file `schemas.py` just converts a record in the database field by field.
+The file `rich_schemas.py` on the other hand converts an `id` in one table to an entire object in the another table, thus the name rich.
+In this way, for example, an entire competition with it's teams, codes, slides and the slides' questions and components can be returned in a single API call.
+
+## Active competitions
+
+Slides, timers and answers needs to be synced during an active presentation.
+This is done using SocketIO together with flask_socketio.
+Events sent is also authorized via json web tokens.
+Whenever client joins a competition they will connect via sockets.
+Only a single instance of a competition can be active at a time.
+All of the functionality related to an active competition and sockets can be found in the file `app/core/sockets.py`.
+
+### Starting and joing presentations
+
+Whenever a client types in a code in the client, the code will be checked via the `api/auth/login/code` API call.
+If there is such a code and it was an operator code, the client will receive a JWT it will need to use to authenticate itself for there on out.
+It will also emit the `start_presentation` event to start the presentation.
+If there is such a code and the associated competition is active, the client will also receive a JWT, regardless if it was an operator code or not.
+In this case the client will instead emit the `join_presentation` event.
+
+### Syncing between clients
+
+The operator will emit `set_slide` and `set_timer` events that syncs their slides and timers between all clients connected to the same presentation.
+The operator can also emit `end_presentation` to end the current presentation, which will disconnect all connected clients.
diff --git a/server/requirements.txt b/server/requirements.txt
index bda47a88036c81470bc7a6b2112b2ecedd904ed7..fe7b13ed57bb2ed619f1b2237f00498c71c67ce0 100644
Binary files a/server/requirements.txt and b/server/requirements.txt differ