diff --git a/controllers/room.py b/controllers/room.py
index ab1821ec31a05afe35907e4df144f0e91230e544..0fcd9ea31292a0e5d116bb7a6970f01b664c5034 100644
--- a/controllers/room.py
+++ b/controllers/room.py
@@ -1,21 +1,25 @@
-from utils import JSON_BODY
+from parameter_util import JSON_BODY, JSON_CONTAINS
+from http_util import FAIL, CODE_JSON, CODE_MISSING, CODE_SEMANTIC
 
-import json
 import random
 
 def create():
     parameters = JSON_BODY(request)
 
     if parameters == None:
-        response.status = 406
-        return(406)
+        return(FAIL(CODE_JSON))
 
-    if not 'player_max' in parameters:
-        response.status = 400
-        return(400)
+    if not JSON_CONTAINS(parameters, [('player_max', int)]):
+        return(FAIL(CODE_MISSING))
 
-    player_max = int(parameters['player_max'])
+    player_max = parameters['player_max']
 
-    ret = {"room_code": str(random.randint(1000,9999)), "room_uid": str(random.randint(10000,99999))}
+    if player_max <= 0:
+        return(FAIL(CODE_SEMANTIC))
 
-    return(response.json(ret)+"\n")
+    room_id = db.Room.insert(player_max=player_max)
+    room_record = db(db.Room.id == room_id).select().first()
+
+    json = {"room_id": room_id, "room_code": room_record.code, "room_pw": room_record.hashcode}
+
+    return(response.json(json))
diff --git a/models/b_dummy.py b/models/b_dummy.py
index 838980eb8cb2f9f481777eaaaea7ea29e9799a9b..3f8e10812dd43af178f72f478a085bc097d3f333 100644
--- a/models/b_dummy.py
+++ b/models/b_dummy.py
@@ -3,6 +3,7 @@ from gluon import current
 current.db = db #Expose db to modules
 current.auth = auth #Expose auth to modules
 current.request = request #Expose request to modules
+current.response = response #Expose response to module
 
 #Reload every module on change
 from gluon.custom_import import track_changes; track_changes(True)
diff --git a/models/f_room.py b/models/f_room.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b985dc0956a3833c25289715543cd8d0f0538fa
--- /dev/null
+++ b/models/f_room.py
@@ -0,0 +1,34 @@
+import random
+from hashlib import blake2b
+
+db.define_table(
+    'Room',
+    Field('code', 'string', notnull=True, requires=IS_NOT_EMPTY(), writable=False, unique=True),
+    Field('hashcode', 'string', notnull=True, requires=IS_NOT_EMPTY(), writable=False),
+    Field('player_max', 'integer', notnull=True, requires=IS_NOT_EMPTY()),
+    Field('creation', 'datetime', default=request.now, notnull=True, requires=IS_NOT_EMPTY()),
+    Field('heartbeat', 'datetime', default=request.now, notnull=True, requires=IS_NOT_EMPTY())
+)
+
+def generateRoomCode(_record):
+    code = None
+
+    while not code or db(db.Room.code == code).count() > 0:
+        code = ""
+        for i in range(4):
+            # Removed characters which could be confused like 0 and O
+            code = code + random.choice("346789ABCDEFGHJKLMNPQRTUVWXY")
+
+    return(code)
+
+def generateHashCode(_record):
+    code = ""
+    digest = blake2b(digest_size=32)
+    digest.update(_record.code.encode("utf-8"))
+    digest.update(str(_record.player_max).encode("utf-8"))
+    digest.update(str(_record.creation).encode("utf-8"))
+    digest.update(str(random.random()).encode("utf-8"))
+    return (digest.hexdigest())
+
+db.Room.code.compute = generateRoomCode
+db.Room.hashcode.compute = generateHashCode
diff --git a/modules/http_util.py b/modules/http_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..e641fd6c4f26d9ca79e2cd50ea246fab59e473dc
--- /dev/null
+++ b/modules/http_util.py
@@ -0,0 +1,9 @@
+from gluon import *
+
+CODE_MISSING = 400
+CODE_JSON = 406
+CODE_SEMANTIC = 422
+
+def FAIL(_code):
+    current.response.status = _code
+    return(_code)
diff --git a/modules/parameter_util.py b/modules/parameter_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..2fed05cf85717d8185fb0d9dcfba5e8a97f86d44
--- /dev/null
+++ b/modules/parameter_util.py
@@ -0,0 +1,29 @@
+from gluon import *
+
+import json
+
+def JSON_BODY(_request):
+    try:
+        ret = json.loads(_request.body.read())
+
+        if not ret:
+            ret = {}
+
+        if not isinstance(ret, dict):
+            return(None)
+
+        return (ret)
+    except json.decoder.JSONDecodeError as error:
+        return (None)
+
+
+def JSON_CONTAINS(_json, _list):
+    for pair in _list:
+
+        element = pair[0]
+        t = pair[1]
+
+        if not element in _json or not isinstance(_json[element], t):
+            return (False)
+
+    return (True)
diff --git a/modules/utils.py b/modules/utils.py
deleted file mode 100644
index bd522b28c46af70dbf992a1a92c98f5b762c3de8..0000000000000000000000000000000000000000
--- a/modules/utils.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from gluon import *
-
-import json
-
-def JSON_BODY(_request):
-    try:
-        ret = json.loads(_request.body.read())
-        if not ret:
-            ret = {}
-
-        return (ret)
-    except json.decoder.JSONDecodeError as error:
-        return (None)