# 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 ) - 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 | |------------------|----------|---------------------------------------------------------------------| | `ADAPTER_API_KEY`| yes | Bearer key that OpenAI clients send. | | `ADMIN_API_KEY` | yes | Bearer key for the admin API / web admin page. | | `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. | Agents are **not** configured via env — they're managed at runtime through the web admin page / admin API and persisted to `AGENTS_FILE`. The store starts empty; add agents after boot. ## 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`: ```bash # 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//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 store is authoritative and persists across restarts; no env config needed. ## Building & running ```bash mix deps.get mix compile ADAPTER_API_KEY=secret AGENTS='{"scholar-agent":"https://n8n.bueso.eu/webhook//chat"}' \ PORT=8000 mix run --no-halt ``` ## Testing ```bash 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.: ```nix inputs.n8n-openai-adapter.url = "git+https://gitea.bueso.eu//n8n-openai-adapter"; ```