Guides & Tutorials11 min read

Using n8n with an Open LLM API: One Credential Field, One Toggle, and the Node That Will Not Work

n8n talks to any OpenAI-compatible endpoint through the Base URL field of its OpenAI credential, and its OpenAI Chat Model node lists whatever /v1/models returns. n8n's OpenAI nodes now default to the Responses API, which most open endpoints answer with a 404; ours serves it, and both the Chat Model and the OpenAI node's Message a Model work. The setup, tested on n8n 2.38, a ticket-triage workflow you can import, and the one Extra Body line that cut the model's answer from 3.3 s to 1.0 s.

The Paralon capybara plugging a glowing cable into the last block of a workflow diagram

n8n is where a lot of "we should use an LLM for this" ideas actually get built, because the person who has the idea can wire a webhook to a model to a spreadsheet in an afternoon. The catch is that n8n's AI nodes are written for OpenAI first, and an open model served behind an OpenAI-compatible endpoint is compatible with most of them, not all. This guide is the short list of what to set and what to avoid, checked against the n8n node source and then run on n8n 2.38.7, plus one workflow that uses the setup for something useful, with the JSON to import.

Everything below was run against qwen3.8-27b on our endpoint, https://paraloncloud.com/v1, but the settings are the same for any server that speaks the OpenAI chat-completions format.

The credential

n8n's OpenAI credential has three fields that matter:

fieldvalue
API Keyyour key (prlc_... for a Paralon key)
Organization ID (optional)leave empty
Base URLhttps://paraloncloud.com/v1

The Base URL field defaults to https://api.openai.com/v1 and is described in the source as "Override the default base URL for the API". When you save the credential, n8n tests it with a GET to /models under that base URL; an endpoint that serves the models list passes, and ours returns:

{"data": [{"id": "qwen3.8-27b", ...}, {"id": "qwen3-3b", ...}]}

That same list is what populates the model dropdown in the chat node, so if the credential test passes you will see your models.

The node to use: OpenAI Chat Model

The OpenAI Chat Model node is the sub-node you attach to an AI Agent, a Basic LLM Chain or a Question and Answer Chain. It reads the base URL from the credential (or from its own Options → Base URL, which overrides the credential) and loads its model dropdown from /models. One detail from the source worth knowing: when the base URL is api.openai.com, n8n filters the list down to chat-capable models; when it is anything else, it shows every model the endpoint returns, so a model that is not a chat model (an image or embedding model on some servers) will appear in the list and fail when selected.

The "Use Responses API" switch. From node version 1.3 the OpenAI Chat Model node has a Use Responses API switch, and its default is on. The Responses API is a separate OpenAI endpoint, /v1/responses, and most OpenAI-compatible servers do not implement it. Against one of those, every run of the node fails with this, which we reproduced on 2.38.7 before our endpoint served it:

404 Cannot POST /v1/responses
Troubleshooting URL: https://docs.langchain.com/oss/javascript/langchain/errors/MODEL_NOT_FOUND/

The troubleshooting link says "model not found", which sends people looking at the model name. The model is fine; it is the endpoint. On such a server, turn the switch off and the node uses /v1/chat/completions. Ours has served /v1/responses since 14 September 2026, so either position works; we ran the triage workflow below both ways with the same answer. What the Responses API guide found running the OpenAI Agents SDK against it is written up separately.

Switch thinking off in Extra Body. The same node has an Options → Extra Body field, described in the source as "additional JSON properties to include in the request body when making requests to OpenAI-compatible APIs". Qwen 3 models think before answering unless told not to, and n8n's node has no switch for that, so this is where it goes. With Use Responses API off:

{"chat_template_kwargs": {"enable_thinking": false}}

With it on:

{"reasoning": {"effort": "low"}}

The effect on the triage workflow below, same ticket, three runs each:

thinking on (default)thinking off via Extra Body
output tokens billed35165
chat-model node time, Responses off3.0 to 3.5 s1.0 to 1.1 s
chat-model node time, Responses on3.1 to 3.6 s1.0 s
resultdamaged_item, highdamaged_item, high

Same answer, a fifth of the output tokens, a third of the time. The thinking tokens never reach n8n (the chat template strips them from the answer), which is why the node's output looks identical while the bill does not. In Responses mode n8n's own counter makes it worse: it showed 45 output tokens for the call that billed 351, because it estimates from the visible text. Take usage from your provider, not from the execution data.

When a custom Base URL is set the node shows a notice: "not all models might be chat-compatible or support other features, like tools calling or JSON response format". For qwen3.8-27b both features work, and the next section uses both.

The OpenAI node: "Message a Model"

The OpenAI node (the regular one, not the chat-model sub-node) was rebuilt as version 2 in n8n 1.117. Its text operation, Message a Model, posts to /responses and nothing else, with no switch, so on an endpoint that serves only chat completions it cannot work. On ours it does: node version 2.3 answered a one-word question in 950 ms. The text came back as "\n\nYellow", two newlines where the hidden reasoning was; trim it in the next node. Its built-in tools (web search, file search, code interpreter) are OpenAI services and are refused with a 400, and its image, audio and file operations are OpenAI-specific. For anything with structured output, the Basic LLM Chain below is still the easier route.

What our endpoint answers, for the record

The chat-model node, through LangChain underneath, sends ordinary chat-completions requests, with tools when an AI Agent has tools attached and stream when streaming is on. We sent the same shapes by hand:

  • Function calling. A request with one tool definition came back with finish_reason: "tool_calls" and a parsed tool_calls array (get_weather with {"city": "Berlin"}), which is what the AI Agent node needs to run a tool and continue.
  • Streaming with usage. stream: true with stream_options.include_usage returned the delta chunks and a final usage chunk, then [DONE].
  • JSON schema output. response_format with json_schema is enforced by guided decoding; the classification guide leaned on it for 7,218 documents without one malformed answer.
  • Embeddings. /v1/embeddings serves multilingual-e5-small, and the Embeddings OpenAI node (version 1.2) works with the same credential: we ran a Default Data Loader → Embeddings OpenAI → In-Memory Vector Store insert on 2.38.7 and the vector came back in 245 ms. One quirk: for a base URL that is not api.openai.com the node skips its "embed" filter on the model list, so the chat models appear in that dropdown too; pick multilingual-e5-small. What the model is good for, and what it is not, is measured in the semantic search guide.
  • Responses API. /v1/responses is served on the same models; details and limits in the Responses API docs.

A workflow: ticket triage with structured output

The setup above is enough to build the usual thing: a webhook receives a support ticket, the model returns an intent, a priority and a one-line summary as JSON, and the rest of the workflow routes on it. The workflow is importable from paralon-ticket-triage.json; it carries a Set node with a sample ticket in place of the webhook and asks you to pick your credential on the chat-model node.

  1. Webhook node, POST, path ticket. The body carries subject and message.

  2. Basic LLM Chain node. Prompt (define below):

    Classify this support ticket.
    Subject: {{ $json.body.subject }}
    Message: {{ $json.body.message }}
    

    Turn on Require Specific Output Format and attach the two sub-nodes below.

  3. OpenAI Chat Model sub-node: the Paralon credential, model qwen3.8-27b, Use Responses API off (on also works; change the Extra Body as above), Sampling Temperature 0, Extra Body {"chat_template_kwargs": {"enable_thinking": false}}.

  4. Structured Output Parser sub-node, JSON Schema mode:

    {
      "type": "object",
      "properties": {
        "intent": {"type": "string", "enum": ["order_status", "cancel_order", "return_request", "refund_status",
                                             "damaged_item", "wrong_item", "payment_failed", "discount_code",
                                             "product_question", "shipping_address_change", "account_login", "complaint"]},
        "priority": {"type": "string", "enum": ["low", "normal", "high"]},
        "summary": {"type": "string", "maxLength": 160}
      },
      "required": ["intent", "priority", "summary"]
    }
    

    The parser appends format instructions to the prompt and validates the answer against the schema; the chain's output item is {"output": {"intent": ..., "priority": ..., "summary": ...}}. Auto-Fix Format re-asks the model when validation fails; in six runs at temperature 0 it was never needed, but it costs nothing to leave on.

  5. Switch node on {{ $json.output.intent }}, or an IF on {{ $json.output.priority }} equals high, and from there Slack, a sheet, your helpdesk.

For the sample ticket ("the zipper is already stuck and the lining is peeling off, I need a replacement or a refund") the chain returned damaged_item, high, and a one-line summary, six runs out of six.

The twelve intents are the taxonomy from the synthetic dataset guide, and the judge guide is the quality check to run on what this workflow produces once it has a few hundred rows: plant wrong intents and see whether a second pass with definitions catches them.

A single ticket through this chain is one request of about 460 input tokens (the parser's format instructions are most of it) and 65 output; at list price ($0.12 and $1.70 per million) that is about $0.00017 per ticket, and the model answers in about a second, so the webhook can respond synchronously.

If you would rather not use the AI nodes

An HTTP Request node does the same job with no sub-nodes and no version surprises:

settingvalue
MethodPOST
URLhttps://paraloncloud.com/v1/chat/completions
AuthenticationGeneric → Header Auth, Authorization: Bearer prlc_...
BodyJSON, below
{
  "model": "qwen3.8-27b",
  "messages": [
    {"role": "system", "content": "Classify the support ticket. Answer via the schema."},
    {"role": "user", "content": "{{ $json.body.subject }}\n\n{{ $json.body.message }}"}
  ],
  "response_format": {"type": "json_schema", "json_schema": {"name": "triage", "schema": { ...the schema above... }}},
  "chat_template_kwargs": {"enable_thinking": false},
  "temperature": 0,
  "max_tokens": 200
}

Then a Code node with return [{json: JSON.parse($input.first().json.choices[0].message.content)}]. This route enforces the schema with guided decoding on the server (response_format) instead of format instructions in the prompt, so the input is about 150 tokens shorter per ticket and the answer cannot fail to parse. The chat completions reference lists the accepted fields.

Checklist

  • Credential: Base URL set to the endpoint, ending in /v1.
  • Credential test passed, which means /models answered.
  • OpenAI Chat Model node: Use Responses API either way; off on servers without /v1/responses.
  • OpenAI Chat Model node: Extra Body with enable_thinking: false.
  • OpenAI node (version 2): not used for text.
  • Embeddings OpenAI: works with the same credential; pick multilingual-e5-small, not a chat model, in its dropdown.
  • Temperature 0 and a JSON schema for anything a downstream node branches on.

A free key from the Console carries a 250,000-token trial, about 470 tickets through the workflow above; after that it is the per-token price on a premium key (billing).

Frequently asked questions

The credential test fails with a 404. The Base URL is missing /v1, or has a trailing path after it. It must be exactly the prefix under which /models and /chat/completions live.

The chat node fails with 404 Cannot POST /v1/responses but the credential test passed. You are pointed at a server without the Responses API. Turn Use Responses API off, or use an endpoint that serves it.

Does the AI Agent node work? It needs function calling, which qwen3.8-27b returns in the standard tool_calls format (we checked the request by hand; the live test above used the Basic LLM Chain, not the Agent). Keep the tool count modest and the descriptions short, the way you would with any model, and test each tool once before relying on the agent to chain them.

Can I use the smaller model for cheap steps? qwen3-3b is on the same endpoint at $0.05 per million input and $0.20 per million output. It appears in the same dropdown; for an enum classification like the triage above it is worth measuring, for the agent keep the 27B.

On the network

A GPU with JupyterLab on it, in about a minute

Billed for the minutes it runs, on a machine we benchmarked before listing it. Or call open models through an OpenAI-compatible API.

Keep reading

Related articles

The Paralon capybara sorting a stream of glowing paper sheets into a row of green trays
Guides & Tutorials

13 min read

Document Classification with an Open LLM API: 7,218 Documents, No Training Data, $0.05 per Thousand

We classified the full 20 Newsgroups test split with Qwen 3.8 27B over an OpenAI-compatible API, zero-shot, ten documents per request, with the label set enforced by a JSON schema. 72% accuracy on twenty classes with no labeled data, 70,000 documents an hour on one key, and a third of all errors inside three categories that overlap by definition. Few-shot examples doubled the cost and moved accuracy half a point.

Read
The Paralon capybara seated as a judge with a glowing gavel, weighing two floating sheets on a holographic balance
Guides & Tutorials

13 min read

LLM-as-a-Judge with an Open Model: 990 Planted Errors, 99.6% Caught, and What the Rubric Changes

We used Qwen 3.8 27B over an OpenAI-compatible API as a judge of dataset labels, and scored it against errors we planted ourselves so every number is exact. With a written rubric the judge caught 491 of 493 wrong labels and also rejected 58 labels the dataset called correct, 43 of them the same systematic mistake in the generator. Without the rubric it agreed with the dataset more and missed 20 planted errors. The rubric is not a tuning knob; it decides what the judge measures.

Read