GET /admin serves a self-contained HTML page (ADMIN_API_KEY protected) that lists agents and lets you add/update/remove them via the admin API — no redeploy needed to add an agent.
n8n-openai-adapter
An OpenAI-compatible HTTP adapter that exposes self-hosted n8n chat agents
behind a standard /v1/chat/completions API, so any OpenAI client (Cursor,
LibreChat, the openai SDK, a custom app) can talk to your n8n agents as if
they were OpenAI models.
n8n itself does not ship an inbound OpenAI-compatible endpoint (its "AI
Gateway" is an outbound proxy to n8n Cloud). This small Elixir service is the
bridge: one /v1/chat/completions endpoint, routed to whichever n8n agent you
name in the model field.
How it works
Your OpenAI client
POST /v1/chat/completions {"model":"scholar-agent","thread_id":"abc","messages":[...]}
|
v
n8n-openai-adapter (Plug + Bandit)
- authorize (Bearer <ADAPTER_API_KEY>)
- look up "scholar-agent" -> n8n chat webhook URL (AgentRegistry GenServer)
- take the last user message
- forward to the n8n webhook {sessionId: thread_id, action: sendMessage, chatInput}
|
v
n8n agent (its MCP tools, memory, etc. run as usual)
|
v
returns OpenAI-shaped {"choices":[{"message":{"role":"assistant","content":...}}]}
Multiple agents = multiple model names, each mapped to a different n8n webhook
in the AGENTS env var.
Configuration (env vars)
| Var | Required | Purpose |
|---|---|---|
AGENTS |
seed | JSON map of model -> n8n chat webhook URL used to seed the store on first boot. e.g. {"scholar-agent":"https://..."} |
ADAPTER_API_KEY |
yes | Bearer key that OpenAI clients send. |
ADMIN_API_KEY |
yes | Bearer key for the admin API (manage agents at runtime). |
AGENTS_FILE |
no | Path to the JSON store (default /var/lib/n8n-openai/agents.json). |
PORT |
no | HTTP port (default 8000). |
CHAT_WEBHOOK_BASIC |
no | "user:password" if your n8n Chat Trigger is Basic-auth protected. |
Admin API (manage agents at runtime)
Agents are persisted to AGENTS_FILE and can be added/removed without a
redeploy, using the ADMIN_API_KEY:
# list
curl -H "Authorization: Bearer $ADMIN_API_KEY" https://openai.bueso.eu/admin/agents
# add / update an agent
curl -X POST -H "Authorization: Bearer $ADMIN_API_KEY" -H "Content-Type: application/json" \
-d '{"model":"media-agent","webhook":"https://n8n.bueso.eu/webhook/<id>/chat"}' \
https://openai.bueso.eu/admin/agents
# remove
curl -X DELETE -H "Authorization: Bearer $ADMIN_API_KEY" \
https://openai.bueso.eu/admin/agents/media-agent
The AGENTS env var only seeds the store on first boot; after that the file is
authoritative.
Building & running
mix deps.get
mix compile
ADAPTER_API_KEY=secret AGENTS='{"scholar-agent":"https://n8n.bueso.eu/webhook/<id>/chat"}' \
PORT=8000 mix run --no-halt
Testing
MIX_ENV=test mix test
Nix
The repo ships a flake.nix exporting overlays.default and a packages.default
(the packaged BEAM release), so it can be consumed as a flake input from your
NixOS config just like any other flake — e.g.:
inputs.n8n-openai-adapter.url = "git+https://gitea.bueso.eu/<owner>/n8n-openai-adapter";