diff --git a/hook.wsgi b/hook.wsgi
new file mode 100644
index 0000000000000000000000000000000000000000..740d30b031f5823c2fc886a6a15c05c603a0a45b
--- /dev/null
+++ b/hook.wsgi
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+# A mini-app for accepting Zoom webhooks
+
+import configparser
+import json
+import psycopg2
+import traceback
+
+### Configuration ###
+
+config = configparser.ConfigParser()
+config.read('zoom.ini')
+
+### Database connection ###
+
+db_connection = None
+db = None
+
+def db_connect():
+    global db_connection, db
+    db_connection = psycopg2.connect(
+                host='localhost',
+                user=config['db']['user'],
+                passwd=config['db']['passwd'],
+                db=config['db']['name'],
+            )
+    db = db_conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
+
+def db_query(query, args=()):
+    if db is None:
+        db_connect()
+    try:
+        db.execute(query, args)
+        # FIXME: Adapt for Postgres!
+    except MySQLdb.OperationalError:
+        # Reconnect if the connection died (timeout, server restart etc.)
+        db_connect()
+        db.execute(query, args)
+
+### Application ###
+
+class HookApp:
+
+    def __init__(self, env, start_response):
+        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 run(self):
+        self.log(self.env)
+
+        method = self.env['REQUEST_METHOD']
+        if method != 'POST':
+            return self.http_error(405, 'Method not allowed', [('Allow', 'POST')])
+
+	# FIXME: Check authorization
+
+        self.log('Gotcha!')
+
+        body = self.env['wsgi.input'].read()
+        js = json.loads(body)
+        self.log(js)
+
+        self.wsgi_start("204 No Content", [])
+        return b""
+
+def application(env, start_response):
+    app = HookApp(env, start_response)
+    try:
+        return app.run()
+    except Exception as exc:
+        app.log(traceback.print_exception(etype=None, value=exc, tb=exc.__traceback__))
+        return app.http_error(500, "Internal server error")
+
+# FIXME: Number of workers and access rights; reloading