diff --git a/authentik/api/templates/api/swagger.html b/authentik/api/templates/api/swagger.html
new file mode 100644
index 000000000..b5e9cc52e
--- /dev/null
+++ b/authentik/api/templates/api/swagger.html
@@ -0,0 +1,22 @@
+{% load static %}
+
+<!doctype html> <!-- Important: must specify -->
+<html>
+    <head>
+        <meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 charecters -->
+        <script type="module" src="{% static 'dist/rapidoc-min.js' %}"></script>
+    </head>
+    <body>
+        <rapi-doc
+            spec-url="{{ path }}"
+            heading-text="authentik"
+            theme="dark"
+            primary-color="#fd4b2d"
+            allow-spec-url-load="false"
+            allow-spec-file-load="false">
+            <div slot="logo">
+                <img src="{% static 'dist/assets/icons/icon.png' %}" style="width:50px; height:50px" />
+            </div>
+        </rapi-doc>
+    </body>
+</html>
diff --git a/authentik/api/templates/rest_framework/api.html b/authentik/api/templates/rest_framework/api.html
deleted file mode 100644
index 2e3e5db96..000000000
--- a/authentik/api/templates/rest_framework/api.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "rest_framework/base.html" %}
-
-{% block title %}{% if name %}{{ name }} – {% endif %}authentik{% endblock %}
-
-{% block branding %}
-<span class='navbar-brand'>
-    authentik
-</span>
-{% endblock %}
-
-{% block style %}
-{{ block.super }}
-<style>
-    body {
-        background-color: #18191a;
-        color: #fafafa;
-    }
-    .prettyprint {
-        background-color: #1c1e21;
-        color: #fafafa;
-        border: 1px solid #2b2e33;
-    }
-    .pln {
-        color: #fafafa;
-    }
-    .well {
-        background-color: #1c1e21;
-        border: 1px solid #2b2e33;
-    }
-</style>
-{% endblock %}
diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py
index b50dea7b6..fea8a58c6 100644
--- a/authentik/api/v2/urls.py
+++ b/authentik/api/v2/urls.py
@@ -1,5 +1,4 @@
 """api v2 urls"""
-from django.conf import settings
 from django.urls import path, re_path
 from drf_yasg2 import openapi
 from drf_yasg2.views import get_schema_view
@@ -11,6 +10,7 @@ from authentik.admin.api.tasks import TaskViewSet
 from authentik.admin.api.version import VersionViewSet
 from authentik.admin.api.workers import WorkerViewSet
 from authentik.api.v2.config import ConfigsViewSet
+from authentik.api.views import SwaggerView
 from authentik.core.api.applications import ApplicationViewSet
 from authentik.core.api.groups import GroupViewSet
 from authentik.core.api.propertymappings import PropertyMappingViewSet
@@ -195,7 +195,9 @@ info = openapi.Info(
 )
 SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,))
 
-urlpatterns = router.urls + [
+urlpatterns = [
+    path("", SwaggerView.as_view(), name="swagger"),
+] + router.urls + [
     path(
         "flows/executor/<slug:flow_slug>/",
         FlowExecutorView.as_view(),
@@ -207,12 +209,3 @@ urlpatterns = router.urls + [
         name="schema-json",
     ),
 ]
-
-if settings.DEBUG:
-    urlpatterns = urlpatterns + [
-        path(
-            "swagger/",
-            SchemaView.with_ui("swagger", cache_timeout=0),
-            name="schema-swagger-ui",
-        ),
-    ]
diff --git a/authentik/api/views.py b/authentik/api/views.py
new file mode 100644
index 000000000..580050ba6
--- /dev/null
+++ b/authentik/api/views.py
@@ -0,0 +1,22 @@
+"""General API Views"""
+from typing import Any
+
+from django.urls import reverse
+from django.views.generic import TemplateView
+
+
+class SwaggerView(TemplateView):
+    """Show swagger view based on rapi-doc"""
+
+    template_name = "api/swagger.html"
+
+    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
+        path = self.request.build_absolute_uri(
+            reverse(
+                "authentik_api:schema-json",
+                kwargs={
+                    "format": ".json",
+                },
+            )
+        )
+        return super().get_context_data(path=path, **kwargs)
diff --git a/authentik/root/settings.py b/authentik/root/settings.py
index f701ace12..37578c44e 100644
--- a/authentik/root/settings.py
+++ b/authentik/root/settings.py
@@ -163,6 +163,9 @@ REST_FRAMEWORK = {
         "authentik.api.auth.AuthentikTokenAuthentication",
         "rest_framework.authentication.SessionAuthentication",
     ),
+    "DEFAULT_RENDERER_CLASSES": [
+        "rest_framework.renderers.JSONRenderer",
+    ],
 }
 
 CACHES = {
diff --git a/web/package-lock.json b/web/package-lock.json
index 39d408e3b..e1e1c211a 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -4,6 +4,14 @@
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
+        "@apitools/openapi-parser": {
+            "version": "0.0.7",
+            "resolved": "https://registry.npmjs.org/@apitools/openapi-parser/-/openapi-parser-0.0.7.tgz",
+            "integrity": "sha512-Bm+GmJ/HIJoNpcwEUSEF9Zh1SqTQ+LsPEK9u5EznVuvoYvVv+dyOWL5/UOAibkNF+wHv7uWS57+NICogPMwzMw==",
+            "requires": {
+                "swagger-client": "^3.13.1"
+            }
+        },
         "@babel/code-frame": {
             "version": "7.10.4",
             "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
@@ -39,6 +47,15 @@
                 "regenerator-runtime": "^0.13.4"
             }
         },
+        "@babel/runtime-corejs3": {
+            "version": "7.13.10",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.10.tgz",
+            "integrity": "sha512-x/XYVQ1h684pp1mJwOV4CyvqZXqbc8CMsMGUnAbuc82ZCdv1U63w5RSUzgDSXQHG5Rps/kiksH6g2D5BuaKyXg==",
+            "requires": {
+                "core-js-pure": "^3.0.0",
+                "regenerator-runtime": "^0.13.4"
+            }
+        },
         "@eslint/eslintrc": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
@@ -655,7 +672,6 @@
             "version": "1.0.10",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
             "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-            "dev": true,
             "requires": {
                 "sprintf-js": "~1.0.2"
             }
@@ -701,6 +717,11 @@
             "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
             "dev": true
         },
+        "asynckit": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+            "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+        },
         "atob": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -792,6 +813,20 @@
                 "fill-range": "^7.0.1"
             }
         },
+        "btoa": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
+            "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
+        },
+        "buffer": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+            "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+            "requires": {
+                "base64-js": "^1.3.1",
+                "ieee754": "^1.2.1"
+            }
+        },
         "buffer-from": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@@ -918,6 +953,17 @@
                 "source-map": "~0.6.0"
             }
         },
+        "clipboard": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
+            "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
+            "optional": true,
+            "requires": {
+                "good-listener": "^1.2.2",
+                "select": "^1.1.2",
+                "tiny-emitter": "^2.0.0"
+            }
+        },
         "cliui": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -987,6 +1033,14 @@
             "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
             "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw=="
         },
+        "combined-stream": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+            "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+            "requires": {
+                "delayed-stream": "~1.0.0"
+            }
+        },
         "commander": {
             "version": "2.20.3",
             "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -1009,12 +1063,30 @@
             "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.16.tgz",
             "integrity": "sha512-gaXX8c0IcLpTNj6q6TCKnhBJ1wuzmWEIrjp3pc1XfXS57WnvB3rVeAvsqYwyGOdPNdZiZYFyCOLgYbKKjhHIFg=="
         },
+        "cookie": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+            "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+        },
         "copy-descriptor": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
             "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
             "dev": true
         },
+        "core-js-pure": {
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz",
+            "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A=="
+        },
+        "cross-fetch": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.0.tgz",
+            "integrity": "sha512-a+yso9lSpXQI9DH+YjAu/m0dVfP8IVoZDPBLLFcvGpeq3KHNdikkekTOdkHiXEuTq4GBOeO0MfWkE40yzF1w7g==",
+            "requires": {
+                "node-fetch": "2.6.1"
+            }
+        },
         "cross-spawn": {
             "version": "7.0.3",
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1047,6 +1119,11 @@
             "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
             "dev": true
         },
+        "deep-extend": {
+            "version": "0.6.0",
+            "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+            "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+        },
         "deep-is": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -1094,6 +1171,17 @@
                 }
             }
         },
+        "delayed-stream": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+            "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+        },
+        "delegate": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+            "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
+            "optional": true
+        },
         "didyoumean2": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/didyoumean2/-/didyoumean2-4.1.0.tgz",
@@ -1332,8 +1420,7 @@
         "esprima": {
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-            "dev": true
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
         },
         "esquery": {
             "version": "1.4.0",
@@ -1556,6 +1643,11 @@
                 "picomatch": "^2.2.1"
             }
         },
+        "fast-json-patch": {
+            "version": "3.0.0-1",
+            "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz",
+            "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw=="
+        },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -1633,6 +1725,16 @@
             "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
             "dev": true
         },
+        "form-data": {
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+            "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+            "requires": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.6",
+                "mime-types": "^2.1.12"
+            }
+        },
         "fragment-cache": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -1740,6 +1842,15 @@
                 "slash": "^3.0.0"
             }
         },
+        "good-listener": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+            "optional": true,
+            "requires": {
+                "delegate": "^3.1.2"
+            }
+        },
         "graceful-fs": {
             "version": "4.2.6",
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
@@ -1824,6 +1935,11 @@
                 "uglify-js": "^3.5.1"
             }
         },
+        "ieee754": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+        },
         "ignore": {
             "version": "5.1.8",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
@@ -1997,6 +2113,14 @@
             "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
             "dev": true
         },
+        "isomorphic-form-data": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz",
+            "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==",
+            "requires": {
+                "form-data": "^2.3.2"
+            }
+        },
         "jest-worker": {
             "version": "26.3.0",
             "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz",
@@ -2035,7 +2159,6 @@
             "version": "3.14.1",
             "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
             "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
-            "dev": true,
             "requires": {
                 "argparse": "^1.0.7",
                 "esprima": "^4.0.0"
@@ -2276,8 +2399,7 @@
         "lodash": {
             "version": "4.17.20",
             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
-            "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
-            "dev": true
+            "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
         },
         "lodash.deburr": {
             "version": "4.1.0",
@@ -2314,6 +2436,11 @@
                 "object-visit": "^1.0.0"
             }
         },
+        "marked": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz",
+            "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw=="
+        },
         "merge-stream": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -2334,6 +2461,19 @@
                 "picomatch": "^2.0.5"
             }
         },
+        "mime-db": {
+            "version": "1.46.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
+            "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ=="
+        },
+        "mime-types": {
+            "version": "2.1.29",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
+            "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
+            "requires": {
+                "mime-db": "1.46.0"
+            }
+        },
         "minify-html-literals": {
             "version": "1.3.5",
             "resolved": "https://registry.npmjs.org/minify-html-literals/-/minify-html-literals-1.3.5.tgz",
@@ -2430,6 +2570,11 @@
                 "lower-case": "^1.1.1"
             }
         },
+        "node-fetch": {
+            "version": "2.6.1",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+            "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+        },
         "object-copy": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -2624,6 +2769,14 @@
             "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
             "dev": true
         },
+        "prismjs": {
+            "version": "1.23.0",
+            "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz",
+            "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==",
+            "requires": {
+                "clipboard": "^2.0.0"
+            }
+        },
         "progress": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -2641,6 +2794,21 @@
             "resolved": "https://registry.npmjs.org/qrjs/-/qrjs-0.1.2.tgz",
             "integrity": "sha1-os38FpElvkCspBIhD5u1g9Bu6c8="
         },
+        "qs": {
+            "version": "6.9.6",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
+            "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ=="
+        },
+        "querystring": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+        },
+        "querystring-browser": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/querystring-browser/-/querystring-browser-1.0.4.tgz",
+            "integrity": "sha1-8uNYgYQKgZvHsb9Zf68JeeZiLcY="
+        },
         "randombytes": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -2658,11 +2826,29 @@
                 "eve-raphael": "0.5.0"
             }
         },
+        "rapidoc": {
+            "version": "8.4.9",
+            "resolved": "https://registry.npmjs.org/rapidoc/-/rapidoc-8.4.9.tgz",
+            "integrity": "sha512-Rh9LWZzBnHoYA0f2kabXDzdsJ5qyCpKNJplk7wqg/kcpQKFqszLMOWPN+zazXKZsTNIjln+8WjZTy0kgsODHEQ==",
+            "requires": {
+                "@apitools/openapi-parser": "^0.0.7",
+                "lit-element": "2.4.0",
+                "lit-html": "1.2.1",
+                "marked": "^2.0.1",
+                "prismjs": "^1.23.0"
+            },
+            "dependencies": {
+                "lit-html": {
+                    "version": "1.2.1",
+                    "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.2.1.tgz",
+                    "integrity": "sha512-GSJHHXMGLZDzTRq59IUfL9FCdAlGfqNp/dEa7k7aBaaWD+JKaCjsAk9KYm2V12ItonVaYx2dprN66Zdm1AuBTQ=="
+                }
+            }
+        },
         "regenerator-runtime": {
             "version": "0.13.7",
             "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
-            "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
-            "dev": true
+            "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
         },
         "regex-not": {
             "version": "1.0.2",
@@ -2921,6 +3107,12 @@
                 "ret": "~0.1.10"
             }
         },
+        "select": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
+            "optional": true
+        },
         "semver": {
             "version": "7.3.2",
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
@@ -3221,8 +3413,7 @@
         "sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-            "dev": true
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         },
         "static-extend": {
             "version": "0.1.2",
@@ -3280,6 +3471,27 @@
                 "has-flag": "^3.0.0"
             }
         },
+        "swagger-client": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.13.1.tgz",
+            "integrity": "sha512-Hmy4+wVVa3kveWzC7PIeUwiAY5qcYbm4XlC4uZ7e5kAePfB2cprXImiqrZHIzL+ndU0YTN7I+9w/ZayTisn3Jg==",
+            "requires": {
+                "@babel/runtime-corejs3": "^7.11.2",
+                "btoa": "^1.2.1",
+                "buffer": "^6.0.3",
+                "cookie": "~0.4.1",
+                "cross-fetch": "^3.0.6",
+                "deep-extend": "~0.6.0",
+                "fast-json-patch": "^3.0.0-1",
+                "isomorphic-form-data": "~2.0.0",
+                "js-yaml": "^3.14.0",
+                "lodash": "^4.17.19",
+                "qs": "^6.9.4",
+                "querystring-browser": "^1.0.4",
+                "traverse": "~0.6.6",
+                "url": "~0.11.0"
+            }
+        },
         "table": {
             "version": "6.0.7",
             "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
@@ -3337,6 +3549,12 @@
             "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
             "dev": true
         },
+        "tiny-emitter": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+            "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+            "optional": true
+        },
         "to-object-path": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -3377,6 +3595,11 @@
                 "is-number": "^7.0.0"
             }
         },
+        "traverse": {
+            "version": "0.6.6",
+            "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
+            "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="
+        },
         "ts-lit-plugin": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/ts-lit-plugin/-/ts-lit-plugin-1.2.1.tgz",
@@ -3519,6 +3742,22 @@
             "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
             "dev": true
         },
+        "url": {
+            "version": "0.11.0",
+            "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+            "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+            "requires": {
+                "punycode": "1.3.2",
+                "querystring": "0.2.0"
+            },
+            "dependencies": {
+                "punycode": {
+                    "version": "1.3.2",
+                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+                    "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+                }
+            }
+        },
         "use": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
diff --git a/web/package.json b/web/package.json
index cb370105b..b78863542 100644
--- a/web/package.json
+++ b/web/package.json
@@ -25,6 +25,7 @@
         "flowchart.js": "^1.15.0",
         "lit-element": "^2.4.0",
         "lit-html": "^1.3.0",
+        "rapidoc": "^8.4.9",
         "rollup": "^2.41.4",
         "rollup-plugin-copy": "^3.4.0",
         "rollup-plugin-cssimport": "^1.0.2",
diff --git a/web/rollup.config.js b/web/rollup.config.js
index 7b7ef95ac..605815b7c 100644
--- a/web/rollup.config.js
+++ b/web/rollup.config.js
@@ -8,6 +8,8 @@ import copy from "rollup-plugin-copy";
 import externalGlobals from "rollup-plugin-external-globals";
 
 const resources = [
+    { src: "node_modules/rapidoc/dist/rapidoc-min.js", dest: "dist/" },
+
     { src: "node_modules/@patternfly/patternfly/patternfly.min.css", dest: "dist/" },
     { src: "src/authentik.css", dest: "dist/" },
 
@@ -16,6 +18,7 @@ const resources = [
     { src: "src/assets/*", dest: "dist/assets" },
     { src: "./icons/*", dest: "dist/assets/icons" },
 ];
+
 // eslint-disable-next-line no-undef
 const isProdBuild = process.env.NODE_ENV === "production";
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types