Local AI Support Ticket Router

Route support tickets using local AI with n8n and Ollama.

Introduction

30 Second Summary

Every support team drowns in tickets. Some are urgent billing disputes, others are simple password resets. Sorting them manually wastes hours that could go toward solving actual problems.

In this project, you will build a local AI-powered support ticket router using n8n and Ollama. You'll send a support message to a webhook, watch Mistral 7B classify it into a category, and see n8n route it down the right path with a tailored response.

What You'll Build

Picture this: you fire off a curl command with a customer complaint, and within seconds get back a structured JSON response telling you it's a billing issue with a suggested response template. All processed entirely on your machine, no cloud services required.

By the end of this project, you'll have:

  • A live webhook endpoint where you POST any support ticket and instantly see it classified by AI running on your own GPU.
  • Conditional routing that automatically sends billing, technical, feature request, and urgent tickets down separate processing branches.
  • Structured JSON responses with the category, reasoning, and a suggested response template for each ticket type.
  • Secret Mission: a priority escalation system that catches critical keywords like "outage" or "data loss" and fast-tracks tickets before AI classification even runs.

Are there any prerequisites?

This project assumes you already have Docker Desktop installed on Windows and Ollama installed locally with GPU access. If you don't have these yet, install them before starting Step 1.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters. Understanding the full picture helps every step make sense as you wire together n8n, Ollama, and Mistral 7B into a working automation.

Set Up n8n in Docker with Ollama Connectivity

To build a ticket router, you need two pieces working together: a local AI model that classifies text, and a workflow engine that orchestrates the routing logic.

In this step, you will get both pieces ready. You will pull the Mistral 7B model into Ollama and spin up n8n inside Docker with the networking bridge that lets the container reach Ollama on your host machine.

In this step, get ready to:
  • Pull the Mistral 7B model in Ollama and verify it responds.
  • Create a Docker Compose file for n8n with the host networking bridge and environment variables.
  • Start n8n and verify you can access the editor at http://localhost:5678.
Pull the Mistral 7B model and verify it responds

Before n8n can classify tickets, it needs a model to talk to. Mistral 7B is a 4.4 GB model that fits comfortably on your RTX 5060's 8 GB of VRAM and supports function calling.

  • Press the Windows key to open your search bar.
  • Type PowerShell and press Enter to open it.
  • Download the Mistral 7B model by running this command:
ollama pull mistral

What does this command do?

The ollama pull command downloads the model weights to your local machine. This is a one-time download of about 4.4 GB. Once pulled, the model stays available for any future use without re-downloading.

You will see a progress bar as the model layers download. Once complete, you should see a success message.

  • Verify the model is ready by sending it a test prompt:
ollama run mistral "Say hello in one sentence"

Mistral responds with a short greeting. The exact wording varies, but you should see a complete sentence back in your terminal.

Not seeing a response?

  • Make sure Ollama is running. Check for the Ollama icon in your system tray. If it is not there, search for Ollama in the Start menu and launch it.
  • If you see a download starting again, wait for it to finish. The pull may not have completed fully on the first attempt.
  • Help me troubleshoot why Mistral is not responding.
Create the Docker Compose file for n8n

n8n runs inside a Docker container, but Ollama runs directly on your machine. For the container to reach Ollama, you need to configure a special networking bridge called host.docker.internal.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir ticket-router
cd ticket-router

PowerShell creates the folder and shows its details. You are now inside the ticket-router folder on your Desktop.

  • Create the Docker Compose configuration file by running:
New-Item docker-compose.yml
  • Open the file in Notepad by running:
notepad docker-compose.yml

Notepad opens with an empty file. This is where you will paste the n8n configuration.

  • Paste the following configuration into the Notepad window:
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n-local
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - NODE_ENV=development
      - GENERIC_TIMEZONE=UTC
      - TZ=UTC
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:

What does this configuration do?

  • image pulls the official n8n Docker image from docker.n8n.io/n8nio/n8n.
  • ports maps port 5678 so you can open the n8n editor in your browser.
  • extra_hosts creates the host.docker.internal hostname inside the container, pointing it back to your host machine where Ollama is running.
  • N8N_RUNNERS_ENABLED=true enables task runners, which is required by n8n (running without them is deprecated).
  • volumes persists your n8n workflows in a Docker volume called n8n_data so they survive container restarts.
  • Save the file in Notepad with Ctrl+S, then close Notepad.
  • Confirm the file was saved correctly by running:
cat docker-compose.yml

You should see the full YAML configuration printed in your terminal, matching what you pasted into Notepad.

Seeing an empty file or an error?

  • If cat shows nothing, re-open the file with notepad docker-compose.yml, paste the YAML again, and make sure you press Ctrl+S before closing.
  • Make sure your PowerShell is still in the ticket-router folder. Run pwd to check your current directory.
  • Help me troubleshoot my docker-compose.yml file.

✔️ Awesome, I've got everything!

Great. Make sure you have saved docker-compose.yml in the ticket-router folder before continuing.

ⓧ I'd like to double check the full code

Your docker-compose.yml should contain exactly this content. Compare against the cat output you just ran:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n-local
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - NODE_ENV=development
      - GENERIC_TIMEZONE=UTC
      - TZ=UTC
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:
Start n8n and verify the editor loads

With the compose file in place, one command spins up the n8n container and pulls the image automatically.

  • Make sure Docker Desktop is running. Check for the Docker whale icon in your system tray. If it is not there, press the Windows key, type Docker Desktop, and press Enter to launch it.
  • Start the n8n container by running this command:
docker compose up -d

What does this command do?

The docker compose up -d command reads your docker-compose.yml, pulls the n8n image if needed, creates the container, and starts it in detached mode (running in the background). The first run downloads the image, which may take a minute or two.

You will see Docker pull the image layers and then report the container as started.

Before you check the result, what do you think you will see when you open http://localhost:5678 in your browser?

  • Open your browser and navigate to http://localhost:5678.

You should see the n8n welcome screen asking you to create an owner account. This confirms n8n is running inside Docker and accessible on your machine.

Seeing a connection error in the browser?

  • Wait 10-15 seconds after running docker compose up -d and refresh the page. n8n takes a moment to initialize on first launch.
  • Run docker ps in PowerShell. You should see n8n-local listed with a status of Up. If it shows Restarting, check the logs with docker logs n8n-local.
  • Make sure Docker Desktop is fully running (not still starting up) before you run the compose command.
  • Help me troubleshoot why n8n is not loading.

Your local AI engine and workflow platform are both running. Next up, you will connect them together inside n8n and send your first prompt to Mistral.

Connect n8n to Ollama and Send Your First Prompt

Your n8n instance is up and running, but it has no idea that Ollama exists on your host machine.

Docker containers are isolated by default. From inside the n8n container, localhost points to itself, not to your Windows host. You need to create a credential that tells n8n exactly where to find Ollama, then prove the connection works by sending a real prompt to Mistral and seeing the response.

In this step, get ready to:
  • Create Ollama credentials in n8n and confirm the connection succeeds.
  • Build a minimal test workflow with a Manual Trigger and Basic LLM Chain node.
  • Execute the workflow and see Mistral's response in the output panel.
Create Ollama credentials in n8n

Credentials in n8n are saved connection details that nodes use to talk to external services. You need one pointing at Ollama's address on your host machine.

  • Switch back to the n8n editor in your browser at http://localhost:5678.
  • In the top right corner, click the dropdown and select Create Credential.
  • Under Add new credential, search for and select Ollama.
  • In the Base URL field, replace the default value with:
http://host.docker.internal:11434
  • Leave the API Key field empty. You do not need it for a local Ollama instance.
  • Click Save.

When you save, n8n automatically tests the connection. You should see a Connection tested successfully message.

Why host.docker.internal instead of localhost?

Inside the n8n container, localhost refers to the container itself. host.docker.internal is a special DNS name that Docker Desktop resolves to your Windows host's IP address. That is where Ollama is listening on port 11434.

The extra_hosts line in your docker-compose.yml from Step 1 ensures this hostname resolves correctly even outside Docker Desktop.

Seeing Connection failed?

Make sure Ollama is still running on your host. Open a new terminal and run ollama list to confirm it responds.

If you see an error mentioning ECONNREFUSED ::1:11434, try changing the Base URL to http://127.0.0.1:11434 instead. This forces IPv4 and avoids an IPv6 resolution issue.

Why can't n8n connect to my local Ollama instance?

Build a test workflow with the Basic LLM Chain node

Now that n8n can reach Ollama, you need a workflow that actually sends a prompt and returns the response. The Basic LLM Chain node is n8n's simplest way to interact with a language model.

  • Click the n8n logo in the top left to return to the main dashboard.
  • Click Create Workflow to open a new blank canvas.
  • Click the + button on the canvas to add your first node.
  • Search for Manual Trigger and select it.
  • Click the + connector on the right side of the Manual Trigger node to add a second node.
  • Search for Basic LLM Chain and select it.

The Basic LLM Chain node needs a language model attached as a sub-node. You will add the Ollama Chat Model.

  • Click on the Basic LLM Chain node to open its settings panel.
  • Under the Model connector at the bottom of the node, click the + to attach a model sub-node.
  • Search for Ollama Chat Model and select it.
  • In the Ollama Chat Model sub-node settings, select your saved Ollama credential from the Credential for Ollama dropdown.
  • In the Model field, type mistral.
  • Close the sub-node panel to return to the Basic LLM Chain settings.

Now set the prompt that the model will respond to.

  • In the Basic LLM Chain node, set Prompt to Define below.
  • In the Prompt (User Message) text area, type:
What are the three most common types of support tickets?

What is the Basic LLM Chain?

The Basic LLM Chain is n8n's simplest AI node. It takes a prompt, sends it to whichever language model you attach, and returns the raw text response.

The Ollama Chat Model is a sub-node that plugs into the chain as the language model provider. This is how n8n keeps the workflow logic (the chain) separate from the model connection (the sub-node).

Execute the workflow and observe the response

Everything is wired up. Time to see if Mistral actually responds through this pipeline.

  • Before you run this, predict what you expect to see. Will the output be plain text, JSON, or something else?
  • Click Test Workflow in the top right corner of the canvas.

n8n executes each node in sequence. After a few seconds, the Basic LLM Chain node shows a green checkmark. Click on it to open the output panel.

You should see Mistral's plain-text response listing common support ticket types. The exact wording varies each run, but the response proves your full pipeline works: n8n triggered, sent the prompt to Ollama on your host, and received the model's output.

Why does it take a few seconds?

Mistral 7B is running locally on your GPU. The first request after the model loads may take slightly longer while Ollama warms up. Subsequent requests in the same session are faster.

Node shows a red error instead of output?

Check that you typed mistral in the Model field exactly. This must match the model name from ollama list.

If the error mentions the prompt being empty, confirm you set Prompt to Define below and entered text in the Prompt (User Message) field.

Help me debug why my Basic LLM Chain node is failing.

Your n8n instance can now talk to Mistral through Ollama. Next up, you will replace the manual trigger with a webhook so external systems can send tickets into this workflow automatically.

Build the Webhook-Triggered Classification Workflow

In the last step, you confirmed that n8n can talk to Ollama and get a response from Mistral. That connection is the foundation for everything else.

A manual trigger is useful for testing, but real automation starts with external events. Any system that needs to classify a support ticket should be able to send it to your workflow over HTTP. In this step, you will swap the Manual Trigger for a Webhook node, craft a system prompt that forces Mistral to return structured JSON classifications, and wire the incoming ticket data into the prompt dynamically.

In this step, get ready to:
  • Replace the Manual Trigger with a Webhook node that accepts POST requests.
  • Write a system prompt that constrains Mistral to return only a JSON object with category and reasoning fields.
  • Inject the ticket subject and body into the prompt using n8n expressions and test the full flow with a curl command.
Replace the Manual Trigger with a Webhook node

The Manual Trigger requires you to click a button inside n8n every time you want to run the workflow. A Webhook node exposes an HTTP endpoint that any external system can call.

  • Switch back to your workflow canvas in the n8n editor.
  • Click the Manual Trigger node to select it.
  • Press Delete (or Backspace) on your keyboard to remove it from the canvas.
  • Click the + button on the canvas (or press Tab) to open the node search panel.
  • Type Webhook and select the Webhook node from the results.

The Webhook node settings panel opens. Configure it to accept POST requests at a predictable path.

  • Set HTTP Method to POST.
  • Set the Path field to ticket.
  • Close the Webhook node panel.
  • Drag the output connector from the Webhook node to the input of your existing Basic LLM Chain node to connect them.

What does the path do?

The path ticket becomes part of the webhook URL. When the workflow is active, n8n listens at http://localhost:5678/webhook/ticket for incoming POST requests.

Your canvas now shows Webhook connected to Basic LLM Chain, with the Ollama Chat Model sub-node still attached underneath.

Craft the classification system prompt

Right now, the Basic LLM Chain sends whatever input it receives to Mistral with no constraints. You need a system prompt that tells Mistral exactly how to behave: classify every ticket into one of four categories, and respond with nothing but a JSON object.

  • Double-click the Basic LLM Chain node to open its settings.
  • Scroll down to the Chat Messages section.
  • Click Add Message.
  • Set the Type to System.
  • Paste the following into the Message field:
You are a support ticket classifier. Given a support ticket with a subject and body, classify it into exactly one of these categories: billing, technical, feature_request, urgent.

Respond with ONLY a valid JSON object. No other text, no markdown, no explanation. The JSON must have exactly these fields:
- "category": one of billing, technical, feature_request, urgent
- "reasoning": a one-sentence explanation of why you chose this category

Example response:
{"category": "billing", "reasoning": "The customer is asking about a charge on their account."}

Why constrain the output so strictly?

LLMs naturally produce free-form text. If you want to route tickets programmatically in the next step, you need the model's response to be machine-parseable JSON every single time. The explicit format instruction and the example teach the model exactly what shape to return.

Including "No other text, no markdown" prevents the model from wrapping its answer in code fences or adding commentary that would break JSON parsing downstream.

Wire the webhook input into the prompt with expressions

The system prompt tells Mistral what to do. The user prompt needs to deliver the actual ticket content. You will use n8n expressions to pull the subject and body from the incoming webhook request.

  • In the Basic LLM Chain node settings, find the Prompt section at the top.
  • Set the Prompt dropdown to Define below.
  • In the Prompt (User Message) text area, click the Expression toggle (the small icon to the right of the field) to switch from fixed text to expression mode.
  • Paste the following expression:
Classify this support ticket:

Subject: {{ $json.body.subject }}
Body: {{ $json.body.body }}

How do n8n expressions work here?

When the webhook receives a POST request with JSON like {"subject": "...", "body": "..."}, n8n stores that payload inside $json.body. The expression {{ $json.body.subject }} pulls the subject value at runtime, and {{ $json.body.body }} pulls the body value.

This means each incoming ticket automatically fills the prompt with its own content. No manual editing needed.

  • Close the Basic LLM Chain node panel by clicking Back to canvas (or pressing Escape).

Before you can test the webhook externally, you need to activate the workflow so n8n starts listening for requests.

  • Click the Active toggle in the top-right corner of the canvas to switch the workflow to active. The toggle turns green.

Before you send a test ticket, what do you think will come back? Mistral should return a JSON object with a category and reasoning field. Let's find out.

  • Open a PowerShell window by pressing the Windows key, typing PowerShell, and pressing Enter.
  • Send a test ticket by running this command:
curl.exe -X POST http://localhost:5678/webhook/ticket -H "Content-Type: application/json" -d '{"subject": "I was charged twice this month", "body": "Hi, I noticed my credit card was billed $49.99 twice on July 15. Can you help me get a refund for the duplicate charge?"}'

Why curl.exe instead of curl?

In PowerShell, curl is an alias for Invoke-WebRequest, which has different syntax. Using curl.exe calls the real curl binary bundled with Windows 10 and later. This gives you the familiar curl flags.

Wait a few seconds for Mistral to process the request. You should see a JSON response in your terminal that looks similar to:

{"category": "billing", "reasoning": "The customer is reporting a duplicate charge and requesting a refund."}

You can also confirm the execution in the n8n editor. Click Executions in the left sidebar to see the most recent run with the classification output.

Not seeing a response?

  • Make sure the workflow is toggled to Active (green toggle in the top-right). Inactive workflows do not listen for webhook requests.
  • Check that you used curl.exe and not bare curl in PowerShell. Bare curl routes to Invoke-WebRequest which needs different syntax.
  • Confirm the URL path matches. The webhook path is ticket, so the full URL is http://localhost:5678/webhook/ticket.
  • If you get a connection error, verify the n8n container is still running with docker ps. You should see n8n-local in the list.

Still stuck? Why isn't my webhook returning a response from Mistral?

Your workflow now accepts tickets from any external system and classifies them using AI. Next up, you will route each category down a separate branch so that different ticket types trigger different actions.

Add Conditional Routing with the Switch Node

Your workflow can classify tickets into categories. But classification alone does not drive action.

In this step, you will parse the LLM's response, route each category down a separate branch using the Switch node, and return tailored acknowledgment responses. This is where n8n's visual workflow builder shines over writing raw code.

In this step, get ready to:
  • Parse the LLM's JSON response to extract the category value.
  • Add a Switch node that routes tickets to four separate outputs.
  • Connect each output to a Respond to Webhook node returning category-specific JSON.
Parse the LLM's JSON response with a Code node

The Basic LLM Chain returns the model's output as a text string. Even though you instructed Mistral to return JSON, n8n treats it as plain text until you explicitly parse it into structured data.

  • In the n8n editor, click the + button after the Basic LLM Chain node on the canvas.
  • Search for Code and select the Code node.
  • Set Language to JavaScript.
  • Set Mode to Run Once for All Items.
  • Replace the default code with the following:
// Get the LLM's text output from the previous node
const llmOutput = $input.first().json.text;

// Parse the JSON string into an object
const parsed = JSON.parse(llmOutput);

// Return the extracted category and reasoning as structured data
return [{ json: { category: parsed.category, reasoning: parsed.reasoning } }];

What does this code do?

The Basic LLM Chain stores its response in a field called text. This code grabs that string with $input.first().json.text, parses it from a JSON string into a JavaScript object, and returns the category and reasoning fields as clean structured output for the next node.

  • Click Test step at the bottom of the Code node panel.
  • You should see output containing a category field (like billing or technical) and a reasoning field in the output panel.

Seeing a JSON parse error?

The LLM might occasionally add extra text around the JSON. Click the Basic LLM Chain node and check the raw output. If the text field contains anything outside the curly braces, adjust your system prompt to be stricter (add "Do NOT include any text outside the JSON object").

If the field name is not text, click the Basic LLM Chain node, look at its output panel, and use the actual field name in your Code node instead.

Help me debug this JSON parse error

Add a Switch node with four category outputs

The Switch node is n8n's branching primitive. It checks a value and sends data down different output paths based on rules you define.

  • Click the + button after the Code node on the canvas.
  • Search for Switch and select it.
  • Leave Mode set to Rules.
  • In the first routing rule, set the data type dropdown to String and the comparison to is equal to.
  • In the Value 1 field, click the expression toggle (the curly braces icon) and enter:
{{ $json.category }}
  • In the Value 2 field, type billing.
  • Toggle Rename Output on and enter billing as the output name.
  • Click Add Routing Rule to add a second rule.
  • Configure the second rule: String, is equal to, Value 1 expression {{ $json.category }}, Value 2 is technical.
  • Toggle Rename Output on and enter technical.
  • Click Add Routing Rule for the third rule.
  • Configure it: String, is equal to, Value 1 expression {{ $json.category }}, Value 2 is feature_request. Rename output to feature_request.
  • Click Add Routing Rule for the fourth rule.
  • Configure it: String, is equal to, Value 1 expression {{ $json.category }}, Value 2 is urgent. Rename output to urgent.

Why four separate rules?

Each rule creates a separate output connector on the Switch node. When the category field matches a rule's value, n8n sends the data down that specific output. This gives each category its own independent processing branch on the canvas.

  • Click Back to canvas to close the Switch node.
  • You should see four output connectors labeled billing, technical, feature_request, and urgent branching out from the Switch node.

Don't see four outputs on the canvas?

Make sure you clicked Add Routing Rule three times (the first rule exists by default, so you end up with four total). Each rule must have Rename Output toggled on with a label entered.

Help me fix my Switch node outputs

Connect each branch to a Respond to Webhook node

Each Switch output needs its own Respond to Webhook node that returns a tailored JSON response for that ticket category.

  • Click the billing output connector on the Switch node and drag to create a new connection.
  • Search for Respond to Webhook and select it.
  • Set Respond With to JSON.
  • In the Response Body field, paste the following JSON:
{
  "category": "billing",
  "suggested_action": "Route to billing team",
  "response_template": "Thank you for reaching out about your billing concern. Our billing team will review your account within 24 hours."
}

What does Respond to Webhook do here?

Each Respond to Webhook node sends its JSON body directly back to the original curl caller as the HTTP response. Because the Webhook node is configured to respond using a Respond to Webhook node, whichever branch executes gets to answer the request.

  • Save the workflow by pressing Ctrl+S.
  • Activate the workflow using the toggle in the top-right corner.
  • Test the billing route by running this command in PowerShell:
curl.exe -X POST http://localhost:5678/webhook/ticket `
  -H "Content-Type: application/json" `
  -d '{"subject": "Incorrect charge on invoice", "body": "I was charged twice for my subscription this month. Please issue a refund for the duplicate charge."}'
  • You should see a JSON response containing "category": "billing" and the billing response template text.

Now add the remaining three Respond to Webhook nodes for the other branches.

  • Drag from the technical output connector to create a new Respond to Webhook node. Set Respond With to JSON and paste this response body:
{
  "category": "technical",
  "suggested_action": "Route to engineering support",
  "response_template": "We have received your technical issue. An engineer will investigate and respond within 4 hours."
}
  • Drag from the feature_request output connector to create another Respond to Webhook node. Set Respond With to JSON and paste this response body:
{
  "category": "feature_request",
  "suggested_action": "Log in product backlog",
  "response_template": "Thanks for your feature suggestion. We have logged it in our product backlog for the team to review."
}
  • Drag from the urgent output connector to create the last Respond to Webhook node. Set Respond With to JSON and paste this response body:
{
  "category": "urgent",
  "suggested_action": "Page on-call engineer immediately",
  "response_template": "URGENT: Your issue has been escalated. An on-call engineer has been paged and will respond within 15 minutes."
}
  • Save the workflow again with Ctrl+S.
  • Make sure the workflow is still active (green toggle in the top-right corner).

Before you test, what do you expect to see if you send a ticket about a login error? Which branch should fire, and what response template should come back?

  • Test the technical route by running this in PowerShell:
curl.exe -X POST http://localhost:5678/webhook/ticket `
  -H "Content-Type: application/json" `
  -d '{"subject": "Cannot log in", "body": "I keep getting a 500 error when trying to access the dashboard. This started after your last update."}'
  • You should see "category": "technical" with the engineering support response template.
  • Test the feature request route:
curl.exe -X POST http://localhost:5678/webhook/ticket `
  -H "Content-Type: application/json" `
  -d '{"subject": "Dark mode request", "body": "It would be great if the app supported dark mode. My eyes get strained during late night sessions."}'
  • You should see "category": "feature_request" with the product backlog response template.
  • Test the urgent route:
curl.exe -X POST http://localhost:5678/webhook/ticket `
  -H "Content-Type: application/json" `
  -d '{"subject": "Complete service outage", "body": "Our entire production environment is down. No users can access the platform. This is critical."}'
  • You should see "category": "urgent" with the escalation response template mentioning the on-call engineer.

Getting an empty response or timeout?

Make sure the workflow is active (green toggle in the top-right). If you deactivated it to edit, the webhook URL stops listening.

If you see an error about the category not matching any rule, check the Switch node's expression. It should be {{ $json.category }} with no extra spaces in the value fields.

Help me debug my routing workflow

Your routing workflow is live and handling four categories. In the Secret Mission, you will add a priority escalation fast-path that catches critical keywords before the LLM even sees them.

Secret mission

Add Priority Escalation Before AI Classification

Real support routing systems need a fast-path for critical issues. What if a ticket says "our database is down" or "we have a data loss incident"? Waiting for the LLM to classify it wastes precious seconds. In this secret mission, you'll add a deterministic keyword check that bypasses AI entirely for emergency tickets, combining rule-based logic with AI judgment in a single workflow.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Docker container n8n-local with the n8n_data volume storing your workflows.
  • Mistral 7B model loaded in Ollama.
  • docker-compose.yml file in your project folder.

Keep everything running

No action needed. Choose this if you're still actively building or experimenting with your workflow.

  • n8n persists your workflows in the n8n_data Docker volume. Your classification and routing workflow will be there every time you open the editor.
  • If Docker Desktop is running, your n8n container stays available at http://localhost:5678.
  • If you restart your machine, the container starts automatically (thanks to restart: unless-stopped in your Compose file).

Pause - I'll come back to this later

Shut down the n8n container to free up system resources. Your workflows stay saved in the Docker volume.

  • Open a terminal and navigate to the folder containing your docker-compose.yml.
  • Stop the container by running:
docker compose down
  • Your workflows are saved in the n8n_data volume. When you want to resume, run:
docker compose up -d
  • The n8n editor will be back at http://localhost:5678 with all your nodes intact.

Delete - I don't want to use this again

Remove all project resources and start fresh. This permanently deletes your n8n workflows and the Mistral model from your machine.

  • Open a terminal and navigate to the folder containing your docker-compose.yml.
  • Remove the n8n container and its data volume by running:
docker compose down -v

What does the -v flag do?

The -v flag tells Docker to also delete the named volume (n8n_data). Without it, your workflows would still be stored on disk even after the container is gone.

  • Remove the Mistral 7B model from Ollama by running:
ollama rm mistral
  • Delete the project folder containing your docker-compose.yml. On macOS or Linux:
rm docker-compose.yml
  • On Windows (PowerShell):
Remove-Item docker-compose.yml
  • Ollama itself remains installed on your machine. You can keep it for future projects or uninstall it through your system's app management settings.

That's a Wrap!

That's a Wrap!

Nice work! You've built a fully local AI-powered support ticket routing system using n8n and Ollama. No cloud APIs, no subscriptions, no data leaving your machine.

You've learned how to:

  • Deploy a self-hosted n8n instance in Docker with networking configured to reach a local Ollama LLM server running Mistral 7B.
  • Build a webhook-triggered workflow that classifies support tickets into categories using a local LLM and a structured system prompt engineered for consistent JSON output.
  • Implement conditional routing with the Switch node to branch workflow logic by AI-determined category, returning tailored responses from each path.
  • Secret Mission: Added a priority escalation fast-path that detects emergency keywords and bypasses AI classification for instant critical-ticket handling.

Ready to quiz yourself?