One signed binary. Every feature compiled in. Free to run. Install Crowkis →
← back to the Roost
curva guidesOctober 3, 2026· 6 min read

n8n AI routing with a Needs review branch

Route n8n items with an LLM: one branch per answer, a Needs review branch for unsure ones, and a Feedback node that calibrates on your data.

For n8n AI routing that doesn't break on a stray full stop, use the `n8n-nodes-curva` community node and its Route operation. Each option you define becomes an output branch, answers below your confidence bar leave on a Needs review branch, and a Feedback node sends the person's answer back so Curva calibrates on your data. There is no text to parse and no Switch node to keep in sync. This guide walks through the setup, the exact branch layout, and the JSON each routed item carries.

In plain words: One Route node replaces an LLM node, a parser and a Switch: options become branches, unsure items go to a person, and the person's answer teaches the router.

The usual pattern is an LLM node that returns a word, followed by a Switch node that matches on it. It works until the model returns "Billing." with a full stop, or "billing / technical", or confidently picks a team for a message that fits none of them. The Route operation removes all three failure points, because the model's answer is mapped onto your option keys before n8n ever sees it.

Install n8n-nodes-curva and add the Curva API credential

You need a self-hosted n8n instance, because community nodes install there, and a running Curva server.

bash
pip install curva-ai
export OPENROUTER_API_KEY=sk-or-v1-...     # any OpenRouter key; free models work

Then start the server with `curva serve`. It listens on `http://localhost:7777` by default. If n8n runs in Docker and Curva on the host, n8n reaches it at `http://host.docker.internal:7777`. If you want the server to require a key, create one with `curva keys create --name n8n`; it prints a `curva_…` key once.

The credential test calls `GET /v1/models`, which checks the key. The `/health` route never needs one, so it can't tell you whether the key works.

Build the n8n AI routing node: options become output branches

Add a Curva node after your trigger (a form, an email, a helpdesk webhook) and pick the Route operation.

The node's outputs now read `billing`, `technical`, `sales`, `none_of_these` and Needs review, in that order. They follow your options as you edit them: like n8n's Switch node, the node computes its outputs from its own parameters. Connect each branch to its queue, such as a helpdesk assignment, a Slack channel or a CRM task.

Route node branches in an n8n AI routing workflow
  1. 1
    Trigger: form, email, webhook
  2. 2
    Curva Route node
  3. 3
    Billing queue
  4. 4
    Technical queue
  5. 5
    Sales queue
  6. 6
    Triage inbox
  7. 7
    Person decides
  8. 8
    Curva Feedback node

Each option is a branch; answers below Min Confidence go to Needs review, which ends in a Feedback node.

What each routed item carries in its curva field

Every routed item keeps its data and gains a `curva` field:

json
{ "curva": { "id": "dec_…", "question": "department", "answer": "billing", "confidence": 0.93,
  "probabilities": { "billing": 0.93, "technical": 0.04, "sales": 0.01, "none_of_these": 0.02 },
  "calibrated": false, "abstain": false } }

`id` is the decision id. Keep it on whatever record you create, because the Feedback step needs it. `probabilities` holds a value for every option, so a downstream node can see when two teams were close. `calibrated` turns `true` once there's enough feedback for that question. The node also adds `model` and `cached`, and `rule` when one of the question's rules answered it.

The Needs review branch: answers below Min Confidence 0.8

Anything below Min Confidence goes to Needs review, whatever the top answer was. That is Curva's `abstain` flag turned into a branch. Put a person there: a Slack message with buttons, an n8n form, or a task in your tracker.

For a yes/no router, pick Yes / No as the question type. The branches are `Yes`, `No` and Needs review. An answer is unsure when neither yes nor no reaches the bar. On this question type, `answer` is `true` or `false` and `probabilities` has a yes and a no value.

Choose the bar by what a wrong route costs. A misrouted sales lead is cheap to fix, so 0.8 may be fine. A refund sent to the wrong queue may need 0.9. Until the question is calibrated, the bar is a guess about the model. After calibration it means what it says.

Close the loop with a Feedback node

End the Needs review branch with another Curva node on Feedback:

The node sends `POST /v1/feedback`. From 30 labels for a question in a project, Curva calibrates its confidences for your data whenever that makes them more accurate. From then on, a 0.8 confidence means about what it says on your items, and the Min Confidence bar sends the right share to people.

Send feedback for a sample of automatically routed items too, not only the reviewed ones. If only the unsure answers get labels, calibration never sees the confident ones. To see the effect, add a Curva node on Calibration Report for the question key and project. It returns `labels`, the fitted calibrator, and before and after accuracy, ECE, Brier score and reliability bins. The after numbers are held out, so they aren't flattered by testing on the labels the calibrator was fitted to.

Several questions per item with Decide

Route handles one question. When an item needs a team, an urgency score and a refund flag at once, use the Decide operation. It takes questions as JSON in the HTTP API format, sends them in one request, and attaches all answers to the item as `curva.answers`, exactly as the server returns them.

json
{
  "team": {"type": "choice", "instructions": "Which team?",
           "options": {"billing": "", "technical": "", "sales": ""}, "min_confidence": 0.8},
  "urgency": {"type": "score", "instructions": "How urgent?",
              "levels": ["low", "normal", "high", "critical"]},
  "refund": {"type": "noul", "instructions": "The customer explicitly asks for a refund"}
}

Route afterwards with an IF or Switch node on the team's choice, and send items whose team answer abstained to review. You can also paste a built-in recipe: `curva recipe show support-triage` prints a ready question set for tickets.

Model, project and options per node

When something fails, the node raises an n8n error carrying Curva's message: a 422 names the bad question, and a rate limit says when to retry. With Settings → On Error → Continue, the failed item goes to the last output (Needs review on Route) with an `error` field, so nothing is dropped silently.

Import the ticket-triage example

The docs include an example workflow, `n8n-ticket-triage.json`. Import it with Workflows → Import from File. Sample tickets go to a Route node with `billing`, `technical`, `sales` and `none_of_these` branches. Unsure tickets go to a review step, a Set node standing in for a Slack or form approval, that ends in a Feedback node. Pick your Curva credential on both Curva nodes and run it.

Next steps

Read the [n8n guide](https://itsmohitrohilla.github.io/curva-docs/guides/n8n/) in the docs and the [node on npm](https://www.npmjs.com/package/n8n-nodes-curva). For the full triage build, see [an n8n ticket triage workflow](/blog/n8n-ticket-triage-workflow/). For the review step itself, read [n8n human in the loop AI](/blog/n8n-human-in-the-loop-ai/). For a single yes/no gate, see [an n8n AI yes/no filter](/blog/n8n-ai-yes-no-filter/), and for more ideas, [LLM classification use cases](/blog/llm-classification-use-cases/).