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:
| field | value |
|---|---|
| API Key | your key (prlc_... for a Paralon key) |
| Organization ID (optional) | leave empty |
| Base URL | https://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 billed | 351 | 65 |
| chat-model node time, Responses off | 3.0 to 3.5 s | 1.0 to 1.1 s |
| chat-model node time, Responses on | 3.1 to 3.6 s | 1.0 s |
| result | damaged_item, high | damaged_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 parsedtool_callsarray (get_weatherwith{"city": "Berlin"}), which is what the AI Agent node needs to run a tool and continue. - Streaming with usage.
stream: truewithstream_options.include_usagereturned the delta chunks and a final usage chunk, then[DONE]. - JSON schema output.
response_formatwithjson_schemais enforced by guided decoding; the classification guide leaned on it for 7,218 documents without one malformed answer. - Embeddings.
/v1/embeddingsservesmultilingual-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 notapi.openai.comthe node skips its "embed" filter on the model list, so the chat models appear in that dropdown too; pickmultilingual-e5-small. What the model is good for, and what it is not, is measured in the semantic search guide. - Responses API.
/v1/responsesis 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.
-
Webhook node,
POST, pathticket. The body carriessubjectandmessage. -
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.
-
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}}. -
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. -
Switch node on
{{ $json.output.intent }}, or an IF on{{ $json.output.priority }}equalshigh, 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:
| setting | value |
|---|---|
| Method | POST |
| URL | https://paraloncloud.com/v1/chat/completions |
| Authentication | Generic → Header Auth, Authorization: Bearer prlc_... |
| Body | JSON, 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
/modelsanswered. - 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.



