diff --git a/hook.wsgi b/hook.wsgi
index 6b7d1c71dc8a7721a0a333b79f650ce3afe5e26a..4c66f0a92106c99bfdb737f2ddfd9cf16a5c98c5 100644
--- a/hook.wsgi
+++ b/hook.wsgi
@@ -40,8 +40,6 @@ def db_query(query, args=()):
 
 ### Utilities ###
 
-# FIXME: Move to shared code?
-
 def parse_time(iso_time):
     return dateutil.parser.isoparse(iso_time)
 
@@ -53,57 +51,32 @@ class HookApp:
         self.env = env
         self.wsgi_start = start_response
 
+
     def log(self, msg):
         print(msg, file=self.env['wsgi.errors'], flush=True)
 
+
     def http_error(self, code, msg, extra_headers=[]):
         self.wsgi_start("{} {}".format(code, msg), extra_headers + [
                 ('Content-Type', 'text/plain')
             ])
         return ["{} {}".format(code, msg)]
 
-    def create_regular_meeting(self, uid, meeting):
-        meeting_id = meeting["id"]
-        self.log(f"Meeting {meeting_id}: Planning")
 
+    def create_schedule(self, mid, meeting_id, occurrence_id, occ):
+        self.log("Meeting {meeting_id}.{occurrence_id}: Scheduling")
         db_query("""
-                INSERT INTO zoom_meetings
-                (meeting_id, uuid, host_id, topic, type, start_time, duration)
-                VALUES (%s, %s, %s, %s, %s, %s, %s)
+                INSERT INTO zoom_schedule
+                (mid, occurrence_id, start_time, duration)
+                VALUES (%s, %s, %s, %s)
             """, (
-                meeting_id,
-                meeting['uuid'],
-                uid,
-                meeting['topic'],
-                meeting['type'],
-                parse_time(meeting['start_time']),
-                meeting['duration'],
+                mid,
+                occurrence_id,,
+                parse_time(occ['start_time']),
+                occ['duration'],
             ))
 
 
-    def create_recurring_meeting(self, uid, meeting):
-        meeting_id = meeting["id"]
-
-        for occ in meeting["occurrences"]:
-            occ_id = occ["occurrence_id"]
-            self.log(f"Meeting {meeting_id}: Planning occurrence {occ_id}")
-
-            db_query("""
-                    INSERT INTO zoom_meetings
-                    (meeting_id, uuid, occurrence_id, host_id, topic, type, start_time, duration)
-                    VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
-                """, (
-                    meeting_id,
-                    meeting['uuid'],
-                    occ_id,
-                    uid,
-                    meeting['topic'],
-                    meeting['type'],
-                    parse_time(occ['start_time']),
-                    occ['duration'],
-                ))
-
-
     def create_meeting(self, js):
         payload = js["payload"]
         meeting = payload["object"]
@@ -116,42 +89,39 @@ class HookApp:
             self.log(f"Meeting {meeting_id}: Host {host_user_id} not found in zoom_users")
             return
 
-        type = meeting["type"]
-        if type == 2:
-            self.create_regular_meeting(user.id, meeting)
-        elif type == 8:
-            self.create_recurring_meeting(user.id, meeting)
-        else:
-            self.log(f"Meeting {meeting_id}: Unknown type {type}")
-            return
-
-
-    def delete_regular_meeting(self, meeting):
-        meeting_id = meeting["id"]
-
-        self.log(f"Meeting {meeting_id}: Deleting")
         db_query("""
-                DELETE FROM zoom_meetings
-                WHERE meeting_id=%s
+                INSERT INTO zoom_meetings
+                (meeting_id, uuid, host_uid, topic, type)
+                VALUES (%s, %s, %s, %s, %s)
+                RETURNING mid
             """, (
                 meeting_id,
+                meeting['uuid'],
+                user.uid,
+                meeting['topic'],
+                meeting['type'],
             ))
+        meeting_row = db.fetchone()
+        mid = meeting_row.mid
+        mtype = meeting["type"]
+        self.log(f"Meeting {meeting_id}: Creating with mid={mid}, type={mtype}")
 
+        if mtype == 8:
+            for occ in meeting["occurrences"]:
+                self.create_schedule(mid, meeting_id, occ["occurrence_id"], meeting)
+        elif 'start_time' in meeting:
+            self.create_schedule(mid, meeting_id, 0, meeting)
 
-    def delete_recurring_meeting(self, meeting):
-        meeting_id = meeting["id"]
-
-        if "occurrences" not in meeting:
-            return self.delete_regular_meeting(meeting)
 
+    def delete_recurring_schedule(self, mid, meeting_id, meeting):
         for occ in meeting["occurrences"]:
             occ_id = occ["occurrence_id"]
-            self.log(f"Meeting {meeting_id}: Deleting occurrence {occ_id}")
+            self.log(f"Meeting {meeting_id}.{occ_id}: Descheduling")
             db_query("""
-                    DELETE FROM zoom_meetings
-                    WHERE meeting_id=%s AND occurrence_id=%s
+                    DELETE FROM zoom_schedule
+                    WHERE mid=%s AND occurrence_id=%s
                 """, (
-                    meeting_id,
+                    mid,
                     occ_id,
                 ))
 
@@ -161,32 +131,27 @@ class HookApp:
         meeting = payload["object"]
         meeting_id = meeting["id"]
 
-        type = meeting["type"]
-        if type == 2:
-            self.delete_regular_meeting(meeting)
-        elif type == 8:
-            self.delete_recurring_meeting(meeting)
-        else:
-            self.log(f"Meeting {meeting_id}: Unknown type {type}")
+        db_query("SELECT * FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
+        meeting_row = db.fetchone()
+        if meeting_row is None:
+            self.log(f"Meeting {meeting_id}: Unknown on delete")
             return
+        mid = meeting_row.mid
+        mtype = meeting_row.type
 
-
-    def update_common_attrs(self, meeting_id, occurrence_id, new):
-        if "topic" in new:
-            self.log(f"Meeting {meeting_id}.{occurrence_id}: Updating topic")
-            db_query("UPDATE zoom_meetings SET topic=%s WHERE meeting_id=%s AND (occurrence_id=%s OR %s = '0')",
-                     (new["topic"], meeting_id, occurrence_id, occurrence_id))
-
-        for a in ['uuid', 'host_id']:
-            if a in new:
-                self.log(f"Meeting {meeting_id}.{occurrence_id}: Change of {a} not supported")
+        if mtype == 8 and "occurrences" in meeting:
+            delete_recurring_schedule(mid, meeting_id, meeting)
+        else:
+            self.log(f"Meeting {meeting_id}: Deleting")
+            db_query("DELETE FROM zoom_schedule WHERE mid=%s", (mid,))
+            db_query("DELETE FROM zoom_meetings WHERE mid=%s", (mid,))
 
 
-    def update_schedule(self, meeting_id, occurrence_id, new):
+    def update_schedule(self, mid, meeting_id, occurrence_id, new):
         if "start_time" in new:
             self.log(f"Meeting {meeting_id}.{occurrence_id}: Updating start time")
-            db_query("UPDATE zoom_meetings SET start_time=%s WHERE meeting_id=%s AND (occurrence_id=%s OR %s = '0')",
-                     (parse_time(new['start_time']), meeting_id, occurrence_id, occurrence_id))
+            db_query("UPDATE zoom_schedule SET start_time=%s WHERE mid=%s AND occurrence_id=%s",
+                     (parse_time(new['start_time']), mid, occurrence_id))
 
         if "duration" in new:
             self.log(f"Meeting {meeting_id}.{occurrence_id}: Updating duration")
@@ -194,85 +159,43 @@ class HookApp:
                      (new['duration'], meeting_id, occurrence_id, occurrence_id))
 
 
-    def update_regular_meeting(self, meeting_id, old, new):
-        self.log(f"Meeting {meeting_id}: Updating regular meeting")
-        self.update_common_attrs(meeting_id, 0, new)
-        self.update_schedule(meeting_id, 0, new)
-
-
-    def update_recurring_meeting_single(self, meeting_id, old, new):
-        self.log(f"Meeting {meeting_id}: Updating single occurrence")
+    def update_meeting_single(self, mid, meeting_id, old, new):
+        self.log(f"Meeting {meeting_id}: Updating single occurrences")
         for occ in new["occurrences"]:
-            self.update_common_attrs(meeting_id, occ["occurrence_id"], new)
-            self.update_schedule(meeting_id, occ["occurrence_id"], new)     # e.g., duration can be set here
-            self.update_schedule(meeting_id, occ["occurrence_id"], occ)
+            self.update_schedule(mid, meeting_id, occ["occurrence_id"], new)     # e.g., duration can be set here
+            self.update_schedule(mid, meeting_id, occ["occurrence_id"], occ)
 
 
-    def update_recurring_meeting_all(self, meeting_id, old, new):
-        meeting_id = new["id"]
+    def update_meeting_all(self, mid, meeting_id, old, new):
         self.log(f"Meeting {meeting_id}: Updating all occurrences")
 
-        if "occurrences" not in new:
-            self.update_common_attrs(meeting_id, 0, new)
-            return
-
-        db_query("SELECT * FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
-        orig = db.fetchone()
-        if orig is None:
-            self.log(f"Meeting {meeting_id}: Update did not find previous version")
-            return
-
-        db_query("DELETE FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
-
-        for occ in new["occurrences"]:
-            occurrence_id = occ["occurrence_id"]
-            self.log(f"Meeting {meeting_id}.{occurrence_id}: Re-creating")
-
-            db_query("""
-                    INSERT INTO zoom_meetings
-                    (meeting_id, uuid, occurrence_id, host_id, topic, type, start_time, duration)
-                    VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
-                """, (
-                    meeting_id,
-                    new.get('uuid', orig.uuid),
-                    occurrence_id,
-                    orig.host_id,
-                    new.get('topic', orig.topic),
-                    new.get('type', orig.type),
-                    parse_time(occ['start_time']),
-                    occ['duration'],
-                ))
-
-
-    def update_regular_to_recurring(self, meeting_id, old, new):
-        self.log(f"Meeting {meeting_id}: Type change to recurring")
-        self.update_recurring_meeting_all(meeting_id, old, new)
-
-
-    def update_recurring_to_regular(self, meeting_id, old, new):
-        self.log(f"Meeting {meeting_id}: Type change to regular")
-
-        db_query("SELECT * FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
-        orig = db.fetchone()
-        if orig is None:
-            self.log(f"Meeting {meeting_id}: Update did not find previous version")
-            return
-
-        db_query("DELETE FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
+        if "occurrences" in new:
+            # So this will be a recurrent meeting, replace all occurrences
+            db_query("DELETE FROM zoom_schedule WHERE mid=%s", (mid,))
+            for occ in meeting["occurrences"]:
+                self.create_schedule(mid, meeting_id, occ["occurrence_id"], new)
+
+        elif "start_time" in new:
+            if new["start_time"] == "":
+                # Descheduling (this can happen in type 3 meetings)
+                self.log(f"Meeting {meeting_id}: Descheduling")
+                db_query("DELETE FROM zoom_schedule WHERE mid=%s", (mid,))
+            elif "duration" in new:
+                # Both start time and duration are set => can safely replacing schedule
+                self.log(f"Meeting {meeting_id}.0: Replacing schedule")
+                db_query("DELETE FROM zoom_schedule WHERE mid=%s", (mid,))
+                self.create_schedule(mid, meeting_id, 0, new)
+            else:
+                # This is just a schedule change
+                self.log(f"Meeting {meeting_id}.0: Rescheduling with new start_time")
+                db_query("UPDATE zoom_schedule SET start_time=%s WHERE mid=%s",
+                         (parse_time(new["start_time"]), mid))
 
-        db_query("""
-                INSERT INTO zoom_meetings
-                (meeting_id, uuid, host_id, topic, type, start_time, duration)
-                VALUES (%s, %s, %s, %s, %s, %s, %s)
-            """, (
-                meeting_id,
-                new.get('uuid', orig.uuid),
-                orig.host_id,
-                new.get('topic', orig.topic),
-                new['type'],
-                parse_time(new['start_time']),
-                new['duration'],
-            ))
+        elif "duration" in new:
+            # This is just a schedule change
+            self.log(f"Meeting {meeting_id}.0: Rescheduling with new duration")
+            db_query("UPDATE zoom_schedule SET duration=%s WHERE mid=%s",
+                     (new["duration"], mid))
 
 
     def update_meeting(self, js):
@@ -281,24 +204,32 @@ class HookApp:
         old = payload["old_object"]
         meeting_id = new["id"]
 
+        db_query("SELECT * FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
+        meeting_row = db.fetchone()
+        if meeting_row is None:
+            self.log(f"Meeting {meeting_id}: Unknown on update")
+            return
+        mid = meeting_row.mid
+
+        old_type = meeting_row.type
         new_type = new.get("type", -1)
-        old_type = old.get("type", -1)
         if old_type != new_type:
-            if old_type == 2 and new_type == 8:
-                self.update_regular_to_recurring(meeting_id, old, new)
-            elif old_type == 8 and new_type == 2:
-                self.update_recurring_to_regular(meeting_id, old, new)
-            else:
-                self.log(f"Meeting {meeting_id}: Unsupported type change from {old_type} to {new_type}")
-            return
+            self.log(f"Meeting {meeting_id}: Transmuting from from type {old_type} to {new_type}")
+            db_query("UPDATE zoom_meetings SET type=%s WHERE mid=%s", (new_type, mid))
+
+        if "topic" in new:
+            self.log(f"Meeting {meeting_id}: Updating topic")
+            db_query("UPDATE zoom_meetings SET topic=%s WHERE mid=%s", (new['topic'], mid))
+
+        for a in ['uuid', 'host_id']:
+            if a in new:
+                self.log(f"Meeting {meeting_id}: Change of {a} not supported")
 
-        scope = payload.get("scope", "")
-        if scope == "":
-            self.update_regular_meeting(meeting_id, old, new)
-        elif scope == "all":
-            self.update_recurring_meeting_all(meeting_id, old, new)
-        elif scope == "single":
-            self.update_recurring_meeting_single(meeting_id, old, new)
+        scope = payload.get("scope", "all")
+        if scope == "single":
+            self.update_meeting_single(mid, meeting_id, old, new)
+        elif scope == all":
+            self.update_meeting_all(mid, meeting_id, old, new)
         else:
             self.log(f"Meeting {meeting_id}: Unsupported update scope {scope}")