diff --git a/lib/n8n_openai_adapter/router.ex b/lib/n8n_openai_adapter/router.ex
index 92fdf8325a..648a0af80f 100644
--- a/lib/n8n_openai_adapter/router.ex
+++ b/lib/n8n_openai_adapter/router.ex
@@ -145,6 +145,18 @@ defmodule N8nOpenaiAdapter.Router do
# --- Admin API (separate ADMIN_API_KEY) ---
+ get "/admin" do
+ case authorize_admin!(conn) do
+ {:halt, conn} ->
+ conn
+
+ conn ->
+ conn
+ |> put_resp_content_type("text/html")
+ |> send_resp(200, admin_page())
+ end
+ end
+
get "/admin/agents" do
case authorize_admin!(conn) do
{:halt, conn} ->
@@ -255,4 +267,111 @@ defmodule N8nOpenaiAdapter.Router do
end
end
end
+
+ # Self-contained admin page: lists agents and lets you add/remove them via
+ # the admin API. Uses the ADMIN_API_KEY (entered in the page) as the Bearer
+ # token for the /admin/agents calls.
+ defp admin_page do
+ """
+
+
+
+
+
+ n8n OpenAI Adapter — Agents
+
+
+
+
n8n OpenAI Adapter — Agents
+
Manage which n8n chat agents are exposed as OpenAI models.
+
+
+
+
+
+
Add / update agent
+
+
+
+
+
+
+
+
+
Configured agents
+
Model
Webhook
+
+
+
+
+ """
+ end
end
diff --git a/test/router_test.exs b/test/router_test.exs
index e810a52943..69da3162a0 100644
--- a/test/router_test.exs
+++ b/test/router_test.exs
@@ -63,6 +63,26 @@ defmodule N8nOpenaiAdapter.RouterTest do
assert conn.status == 400
end
+ test "GET /admin serves the web admin page (admin key)" do
+ conn =
+ conn(:get, "/admin")
+ |> put_req_header("authorization", "Bearer test-admin-key")
+ |> Router.call(Router.init([]))
+
+ assert conn.status == 200
+ assert conn.resp_body =~ "n8n OpenAI Adapter"
+ assert conn.resp_body =~ "admin/agents"
+ end
+
+ test "GET /admin rejects a non-admin key" do
+ conn =
+ conn(:get, "/admin")
+ |> put_req_header("authorization", "Bearer test-key")
+ |> Router.call(Router.init([]))
+
+ assert conn.status == 401
+ end
+
# --- Admin API ---
test "GET /admin/agents lists agents (admin key)" do