diff --git a/stechuhr_server/server.py b/stechuhr_server/server.py
index 4fef843f8a5c2ec0dafd9cd73f16acbb7499e219..decebaa05246c5cf0e369c10f22d14ce0181b680 100644
--- a/stechuhr_server/server.py
+++ b/stechuhr_server/server.py
@@ -1,11 +1,11 @@
 #!/usr/bin/env python 
 #-*- coding: utf-8 -*-
+import re
 import datetime as dt
 import sqlite3
 from flask import Flask, request
 from logging.config import dictConfig
 
-
 from .config import initialize_config
 
 
@@ -21,9 +21,13 @@ ignore_get_requests = false
 # sqlite db path, use ":memory:" for RAM db
 path = "visitors.db"
 
-# minimum and maximum lengths
-min_id_length = 6
-max_id_length = 24
+# A list of possible regex patterns for the id (logical OR!)
+id_patterns = [
+    "[A-z0-9]{24}",
+    "[A-Z0-9]{6,8}",
+]
+
+# minimum and maximum lengths for the received strings
 min_entrance_length = 1
 max_entrance_length = 128
 min_place_length = 1
@@ -38,7 +42,11 @@ format = "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
 # Initialize the configuration (create a default one if needed)
 config = initialize_config(APPLICATION_NAME, DEFAULT_CONFIG)
 
+# Compile the patterns for the ids once at startup
+config["database"]["id_patterns"] = [re.compile(p) for p in config["database"]["id_patterns"]]
 
+# Config for the logger, there should be no need to make
+# manual changes here
 dictConfig({
     'version': 1,
     'formatters': {'default': {
@@ -56,7 +64,11 @@ dictConfig({
 })
 
 
+# Initialization
 app = Flask(APPLICATION_NAME)
+
+# Create an initial connection and create the needed tables if they
+# don't exist yet.
 conn = sqlite3.connect(config["database"]["path"])
 cursor = conn.cursor()
 sqlite_create_table_query = '''CREATE TABLE visitors (
@@ -71,7 +83,6 @@ sqlite_create_table_query = '''CREATE TABLE visitors (
 try:
     app.logger.info('Constructing table for visitors')
     cursor.execute(sqlite_create_table_query)
-    app.logger.info('Constructed table for visitors')
 except sqlite3.OperationalError as e:
     app.logger.warning(e)
     pass
@@ -82,12 +93,21 @@ def register_movement(data: dict, conn, cursor):
     """
     Construct and store a new movment in the database
     """
+    # Create a timestamp
     data["time"] = dt.datetime.now()
+
+    # Construct an ID for the current movement
     data = construct_movement_id(data)
+
+    # 1. Draft SQL-Statement
     sqlite_insert_with_param = """INSERT INTO 'visitors'
                           ('movement_id', 'id', 'place', 'entrance', 'direction', 'time') 
                           VALUES (?, ?, ?, ?, ?, ?);"""
+    
+    # 2. Draft parameters
     data_tuple = (data["movement_id"], data["id"], data["place"], data["entrance"], data["direction"], data["time"])
+
+    # Execute both and commit to db
     cursor.execute(sqlite_insert_with_param, data_tuple)
     conn.commit()
 
@@ -95,11 +115,19 @@ def register_movement(data: dict, conn, cursor):
 def construct_movement_id(data: dict) -> dict:
     """
     Generate and set the "movement_id" for a given data dict
+    Needs to be collision proof for the db.
+    Looks like: 2020-11-12T07:10:49.81--543GFDHGfddf455-lerchenfeld_Haupteingang/in
     """
+    # If data["time"] is not set, raise Error
     if not "time" in data.keys():
         raise ValueError("No \"time\" key set yet. run construct_movement_id only after setting time")
+    
+    # Use isoformat time like 2020-11-12T07:06:08.595770 ...
     time = data["time"].isoformat()
-    movement_id = "{}--{}-{}/{}/{}".format(time, data["id"], data["place"], data["entrance"], data["direction"])
+    # ... but limit the precision of sub seconds to two places
+    time = "{}.{}".format(time.rsplit(".")[0], time.rsplit(".")[1][:2])
+    # Construct final movement ID.
+    movement_id = "{}--{}-{}_{}/{}".format(time, data["id"], data["place"], data["entrance"], data["direction"])
     data["movement_id"] = movement_id
     return data
 
@@ -108,9 +136,11 @@ def get_records(conn, cursor):
     """
     Get all existing movement records
     """
-    sqlite_select_query = """SELECT place, entrance, direction, time, id from visitors where movement_id = ?"""
+    # Draft select query
+    sqlite_select_query = """SELECT place, entrance, direction, time, id from visitors"""
     try:
-        cursor.execute(sqlite_select_query, (1,))
+        # Execute query and fill visitor with results
+        cursor.execute(sqlite_select_query)
         records = cursor.fetchall()
         visitors = [{"place":r[0], "entrance":r[1], "direction":r[2], "time":r[3], "id":r[4]} for r in records]
     except sqlite3.OperationalError as e:
@@ -120,8 +150,6 @@ def get_records(conn, cursor):
     return visitors
 
     
-
-
 def is_valid_data(data: dict) -> bool:
     """
     Check if the body data of the request is valid
@@ -144,21 +172,41 @@ def is_valid_data(data: dict) -> bool:
         return False
 
     # Basic length check for place
-    if len(data["place"]) >= max_place_length:
-        app.logger.info('400, JSON "place"-key was too long: was {} should be <= {}'.format(len(data["place"]), max_entrance_length))
+    if not length_check(data, "place", config["database"]["min_place_length"], config["database"]["max_place_length"]):
         return False
 
     # Basic length check for entrance
-    if len(data["entrance"]) >= max_entrance_length:
-        app.logger.info('400, JSON "entrance"-key was too long: was {} should be <= {}'.format(len(data["entrance"]), max_entrance_length))
+    if not length_check(data, "entrance", config["database"]["min_entrance_length"], config["database"]["max_entrance_length"]):
         return False
 
-    # Basic length check for ID
-    if len(data["id"]) >= max_id_length:
-        app.logger.info('400, JSON "id"-key was too long: was {} should be <= {}'.format(len(data["id"]), max_id_length))
+    # Match regex patterns with visitor ID
+    if not id_pattern_check(data["id"]):
+        app.logger.info('JSON "id"-value didn\'t match any pattern: {}'.format(data["id"]))
         return False
 
-    # todo: Add more concise checks for the ID
+    return True
+
+
+def id_pattern_check(visitor_id: str) -> bool:
+    """
+    Returns True if any of the patterns from the config matches.
+    Returns False if none of the patterns matches.
+    """
+    return any([re.match(p, visitor_id) for p in config["database"]["id_patterns"]])
+
+
+def length_check(data: dict, key: str, minimum: int, maximum: int) -> bool:
+    """
+    Returns True if the given data[key] value length is within
+    minimum and maximum. False and a log message otherwise.
+    """
+    # Basic length check for ID (max)
+    if len(data[key]) >= maximum:
+        app.logger.info('JSON "{}"-value was too long: was {} should be <= {}'.format(key, len(data[key]), maximum))
+        return False
+    if len(data[key]) <= minimum:
+        app.logger.info('JSON "{}"-value was too short: was {} should be >= {}'.format(key, len(data[key]), minimum))
+        return False
     return True
 
 
diff --git a/test.sh b/test.sh
index f5b33ce35f6b7d4dd00d12e91ef230dcbd9983cc..a6f7fb796a9332b9ea5a392e76e0b935f4e31f43 100644
--- a/test.sh
+++ b/test.sh
@@ -1,3 +1,3 @@
 #! /bin/bash
 
-curl --header "Content-Type: application/json"  --request POST --data '{"place":"lerchenfeld", "entrance":"Haupteingang", "direction":"in", "id":"543GFDHGfddf455"}' http://localhost:5000/
+curl --header "Content-Type: application/json"  --request POST --data '{"place":"lerchenfeld", "entrance":"haupteingang", "direction":"in", "id":"543GFDHGfddf455"}' http://localhost:5000/