diff --git a/check_routes.py b/check_routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a577c037fb3c0078cfce2fb422e5abf7558ca0c
--- /dev/null
+++ b/check_routes.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+import regex as re
+import os
+import sys
+
+ROUTE_PATTERN = r'@(?:[[:alpha:]])+\.route\(\"(?<url>[^"]+)"[^)]*\)\s*(?:@[[:alpha:]_()., ]+\s*)*def\s+(?<name>[[:alpha:]][[:alnum:]_]*)\((?<params>[[:alnum:], ]*)\):'
+quote_group = "[\"']"
+URL_FOR_PATTERN = r'url_for\({quotes}(?<name>[[:alpha:]][[:alnum:]_]*){quotes}'.format(quotes=quote_group)
+
+ROOT_DIR = "."
+ENDINGS = [".py", ".html", ".txt"]
+MAX_DEPTH = 2
+
+def list_dir(dir, level=0):
+    if level >= MAX_DEPTH:
+        return
+    for file in os.listdir(dir):
+        path = os.path.join(dir, file)
+        if os.path.isfile(path):
+            if file == sys.argv[0]:
+                continue
+            for ending in ENDINGS:
+                if file.endswith(ending):
+                    yield path
+        elif os.path.isdir(path):
+            yield from list_dir(path, level+1)
+
+class Route:
+    def __init__(self, file, name, parameters):
+        self.file = file
+        self.name = name
+        self.parameters = parameters
+
+    def __repr__(self):
+        return "Route({file}, {name}, {parameters})".format(
+            file=self.file, name=self.name, parameters=self.parameters)
+
+    def get_parameter_set(self):
+        return {parameter.name for parameter in self.parameters}
+
+class Parameter:
+    def __init__(self, name, type=None):
+        self.name = name
+        self.type = type
+
+    def __repr__(self):
+        return "Parameter({name}, {type})".format(name=self.name, type=self.type)
+
+    @staticmethod
+    def from_string(text):
+        if ":" in text:
+            type, name = text.split(":", 1)
+            return Parameter(name, type)
+        return Parameter(text)
+
+def split_url_parameters(url):
+    params = []
+    current_param = None
+    for char in url:
+        if current_param is None:
+            if char == "<":
+                current_param = ""
+        else:
+            if char == ">":
+                params.append(Parameter.from_string(current_param))
+                current_param = None
+            else:
+                current_param += char
+    return params
+
+def split_function_parameters(parameters):
+    return list(map(str.strip, parameters.split(",")))
+
+def read_url_for_parameters(content):
+    params = []
+    bracket_level = 1
+    current_param = None
+    for char in content:
+        if bracket_level == 0:
+            if current_param is not None:
+                params.append(current_param.split("=")[0].strip())
+            return params
+        if char == ",":
+            if current_param is not None:
+                params.append(current_param.split("=")[0].strip())
+            current_param = ""
+        else:
+            if current_param is not None:
+                current_param += char
+            if char == "(":
+                bracket_level += 1
+            elif char == ")":
+                bracket_level -= 1
+
+class UrlFor:
+    def __init__(self, file, name, parameters):
+        self.file = file
+        self.name = name
+        self.parameters = parameters
+
+    def __repr__(self):
+        return "UrlFor(file={file}, name={name}, parameters={parameters})".format(
+            file=self.file, name=self.name, parameters=self.parameters)
+
+routes = {}
+url_fors = []
+for file in list_dir(ROOT_DIR):
+    with open(file, "r") as infile:
+        content = infile.read()
+        for match in re.finditer(ROUTE_PATTERN, content):
+            name = match.group("name")
+            function_parameters = split_function_parameters(match.group("params"))
+            url_parameters = split_url_parameters(match.group("url"))
+            routes[name] = Route(file, name, url_parameters)
+        for match in re.finditer(URL_FOR_PATTERN, content):
+            name = match.group("name")
+            begin, end = match.span()
+            parameters = read_url_for_parameters(content[end:])
+            url_fors.append(UrlFor(file=file, name=name, parameters=parameters))
+
+for url_for in url_fors:
+    if url_for.name not in routes:
+        print("Missing route '{}' (for url_for in '{}')".format(url_for.name, url_for.file))
+        continue
+    route = routes[url_for.name]
+    route_parameters = route.get_parameter_set()
+    url_parameters = set(url_for.parameters)
+    if len(route_parameters ^ url_parameters) > 0:
+        print("Parameters not matching for '{}' in '{}:'".format(url_for.name, url_for.file))
+        only_route = route_parameters - url_parameters
+        only_url = url_parameters - route_parameters
+        if len(only_route) > 0:
+            print("Only in route: {}".format(only_route))
+        if len(only_url) > 0:
+            print("Only in url: {}".format(only_url))
diff --git a/server.py b/server.py
index 7be156bb55a40f4e470108261c76c6a22b56f365..70a88f58434d8cb5486afca56656b68729bc2a23 100755
--- a/server.py
+++ b/server.py
@@ -350,7 +350,7 @@ def list_protocols():
     protocoltype = None
     protocoltype_id = None
     try:
-        protocoltype_id = int(request.args.get("protocoltype"))
+        protocoltype_id = int(request.args.get("protocoltype_id"))
     except (ValueError, TypeError):
         pass
     search_term = request.args.get("search")
@@ -456,7 +456,7 @@ def new_protocol():
         db.session.commit()
         tasks.push_tops_to_calendar(protocol)
         return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
-    type_id = request.args.get("type_id")
+    type_id = request.args.get("protocoltype_id")
     if type_id is not None:
         form.protocoltype.data = type_id
         upload_form.protocoltype.data = type_id
@@ -731,7 +731,7 @@ def list_todos():
     protocoltype = None
     protocoltype_id = None
     try:
-        protocoltype_id = int(request.args.get("protocoltype"))
+        protocoltype_id = int(request.args.get("protocoltype_id"))
     except (ValueError, TypeError):
         pass
     search_term = request.args.get("search")
@@ -774,7 +774,7 @@ def list_todos():
 @login_required
 def new_todo():
     user = current_user()
-    protocoltype_id = optional_int_arg("type_id")
+    protocoltype_id = optional_int_arg("protocoltype_id")
     protocol_id = optional_int_arg("protocol_id")
     protocoltype = ProtocolType.query.filter_by(id=protocoltype_id).first()
     protocol = Protocol.query.filter_by(id=protocol_id).first()
@@ -819,7 +819,7 @@ def edit_todo(todo):
     if form.validate_on_submit():
         form.populate_obj(todo)
         db.session.commit()
-        return redirect(request.args.get("next") or url_for("list_todos", protocoltype=todo.protocoltype.id))
+        return redirect(request.args.get("next") or url_for("list_todos", protocoltype_id=todo.protocoltype.id))
     return render_template("todo-edit.html", form=form, todo=todo)
 
 @app.route("/todo/show/<int:todo_id>")
@@ -839,7 +839,7 @@ def delete_todo(todo):
     db.session.delete(todo)
     db.session.commit()
     flash("Todo gelöscht.", "alert-success")
-    return redirect(request.args.get("next") or url_for("list_todos", protocoltype=type_id))
+    return redirect(request.args.get("next") or url_for("list_todos", protocoltype_id=type_id))
 
 @app.route("/todo/merge", methods=["GET", "POST"])
 @login_required
@@ -861,7 +861,7 @@ def merge_todos():
             db.session.delete(todo2)
             db.session.commit()
             flash("Merged todos {} and {}.".format(id1, id2), "alert-success")
-            return redirect(request.args.get("next") or url_for("show_todos"))
+            return redirect(request.args.get("next") or url_for("list_todos"))
     return render_template("todos-merge.html", form=form, next_url=request.args.get("next"))
 
 @app.route("/decisions/list")
diff --git a/templates/default-top-edit.html b/templates/default-top-edit.html
index fa90f218726998b7ca33f6e44b12e3f0351bf163..6ead1bb4d078987aaa41ea2318b7f57cc2996fc4 100644
--- a/templates/default-top-edit.html
+++ b/templates/default-top-edit.html
@@ -4,6 +4,6 @@
 
 {% block content %}
 <div class="container">
-    {{render_form(form, action_url=url_for("edit_default_top", type_id=protocoltype.id, defaulttop_id=defaulttop.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
+    {{render_form(form, action_url=url_for("edit_default_top", protocoltype_id=protocoltype.id, defaulttop_id=defaulttop.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
 </div>
 {% endblock %}
diff --git a/templates/default-top-new.html b/templates/default-top-new.html
index be99948f73f23dbf276eacc3f350a0bfa4f82686..147712c68b3f9fe2e7ada6b8ab4e64fa60efc2f1 100644
--- a/templates/default-top-new.html
+++ b/templates/default-top-new.html
@@ -4,6 +4,6 @@
 
 {% block content %}
 <div class="container">
-    {{render_form(form, action_url=url_for("new_default_top", type_id=protocoltype.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Anlegen")}}
+    {{render_form(form, action_url=url_for("new_default_top", protocoltype_id=protocoltype.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Anlegen")}}
 </div>
 {% endblock %}
diff --git a/templates/defaultmeta-edit.html b/templates/defaultmeta-edit.html
index 14385be6aac943e80b5348f15709adeb65976502..52cc5131ee38c6e3cff15c7df88ba17b4e4aea5f 100644
--- a/templates/defaultmeta-edit.html
+++ b/templates/defaultmeta-edit.html
@@ -4,6 +4,6 @@
 
 {% block content %}
 <div class="container">
-    {{render_form(form, action_url=url_for("edit_defaultmeta", meta_id=meta.id, next=url_for("show_type", protocoltype_id=meta.protocoltype.id)), action_text="Ändern")}}
+    {{render_form(form, action_url=url_for("edit_defaultmeta", defaultmeta_id=meta.id, next=url_for("show_type", protocoltype_id=meta.protocoltype.id)), action_text="Ändern")}}
 </div>
 {% endblock %}
diff --git a/templates/reminder-edit.html b/templates/reminder-edit.html
index 08fcb2f6d8b64f6408f2c81eed3f010c3a248951..46913fd0719d766157d4efcce6f4192eb58c17da 100644
--- a/templates/reminder-edit.html
+++ b/templates/reminder-edit.html
@@ -4,6 +4,6 @@
 
 {% block content %}
 <div class="container">
-    {{render_form(form, action_url=url_for("edit_reminder", protocoltype_id=protocoltype.id, meetingreminder_id=meetingreminder.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
+    {{render_form(form, action_url=url_for("edit_reminder", meetingreminder_id=meetingreminder.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
 </div>
 {% endblock %}
diff --git a/views/tables.py b/views/tables.py
index b159f5d0adb1177545693edfd094521a66ef22f7..9d40aa02b85a4299fb8d134a3d5efd50f69a710d 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -124,7 +124,7 @@ class ProtocolTypesTable(Table):
         has_modify_right = protocoltype.has_modify_right(user)
         protocoltype_link = url_for("show_type", protocoltype_id=protocoltype.id)
         protocol_link = url_for("show_protocol", protocol_id=protocol.id)
-        new_protocol_link = url_for("new_protocol", type_id=protocoltype.id)
+        new_protocol_link = url_for("new_protocol", protocoltype_id=protocoltype.id)
         mobile_name = "{} ({})".format(protocoltype.name, protocoltype.short_name)
         mobile_links = [Table.link(protocol_link, protocol.get_identifier())]
         if has_modify_right:
@@ -221,10 +221,10 @@ class DefaultTOPsTable(Table):
             top.name,
             top.number,
             Table.concat([
-                Table.link(url_for("move_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id, diff=1), "Runter"),
-                Table.link(url_for("move_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id, diff=-1), "Hoch"),
+                Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=1), "Runter"),
+                Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=-1), "Hoch"),
                 Table.link(url_for("edit_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id), "Ändern"),
-                Table.link(url_for("delete_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id), "Löschen", confirm="Bist du dir sicher, dass du den Standard-TOP {} löschen willst?".format(top.name))
+                Table.link(url_for("delete_default_top", defaulttop_id=top.id), "Löschen", confirm="Bist du dir sicher, dass du den Standard-TOP {} löschen willst?".format(top.name))
             ])
         ]
 
@@ -244,8 +244,8 @@ class MeetingRemindersTable(Table):
             reminder.additional_text or ""
         ]
         action_links = [
-            Table.link(url_for("edit_reminder", protocoltype_id=self.protocoltype.id, meetingreminder_id=reminder.id), "Ändern"),
-            Table.link(url_for("delete_reminder", protocoltype_id=self.protocoltype.id, meetingreminder_id=reminder.id), "Löschen", confirm="Bist du dir sicher, dass du die Einladungsmail {} Tage vor der Sitzung löschen willst?".format(reminder.days_before))
+            Table.link(url_for("edit_reminder", meetingreminder_id=reminder.id), "Ändern"),
+            Table.link(url_for("delete_reminder", meetingreminder_id=reminder.id), "Löschen", confirm="Bist du dir sicher, dass du die Einladungsmail {} Tage vor der Sitzung löschen willst?".format(reminder.days_before))
         ]
         action_part = [Table.concat(action_links)]
         return general_part + action_part
@@ -317,7 +317,7 @@ class TodosTable(Table):
             todo.get_state(),
             Table.link(url_for("show_protocol", protocol_id=protocol.id), protocol.get_identifier())
                 if protocol is not None
-                else Table.link(url_for("list_protocols", protocoltype=todo.protocoltype.id), todo.protocoltype.short_name),
+                else Table.link(url_for("list_protocols", protocoltype_id=todo.protocoltype.id), todo.protocoltype.short_name),
             todo.who,
             todo.description,
         ]
@@ -437,7 +437,7 @@ class DefaultMetasTable(Table):
         ]
         links = [
             Table.link(url_for("edit_defaultmeta", defaultmeta_id=meta.id), "Ändern"),
-            Table.link(url_for("delete_defaultmeta", defaultmeta_id=meta.id, confirm="Bist du dir sicher, dass du das Metadatenfeld {} löschen willst?".format(meta.name)), "Löschen")
+            Table.link(url_for("delete_defaultmeta", defaultmeta_id=meta.id), confirm="Bist du dir sicher, dass du das Metadatenfeld {} löschen willst?".format(meta.name), "Löschen")
         ]
         link_part = [Table.concat(links)]
         return general_part + link_part