diff --git a/workflows/Classify and route customer feedback with GPT-4o, Gemini, and guardrails-13855/readme-13855.md b/workflows/Classify and route customer feedback with GPT-4o, Gemini, and guardrails-13855/readme-13855.md
new file mode 100644
index 000000000..ec6ef17ad
--- /dev/null
+++ b/workflows/Classify and route customer feedback with GPT-4o, Gemini, and guardrails-13855/readme-13855.md
@@ -0,0 +1,928 @@
+Classify and route customer feedback with GPT-4o, Gemini, and guardrails
+
+https://n8nworkflows.xyz/workflows/classify-and-route-customer-feedback-with-gpt-4o--gemini--and-guardrails-13855
+
+
+# Classify and route customer feedback with GPT-4o, Gemini, and guardrails
+
+# 1. Workflow Overview
+
+This workflow receives customer feedback through a webhook, normalizes the payload, screens the input with AI guardrails, classifies the feedback with an LLM, validates the generated structure, screens the drafted response again, and finally routes the item to a deterministic destination based on category and confidence.
+
+Its main use case is automated triage of inbound customer feedback from a web form or similar source. It combines deterministic validation and routing with AI-based classification and drafting, while adding safety layers before and after model generation.
+
+## 1.1 Intake & Normalization
+
+The workflow starts with a webhook that accepts POST requests, then transforms inconsistent incoming field names into a standardized internal schema. It also checks whether the minimum required information is present.
+
+## 1.2 Input Guardrails
+
+Before the feedback reaches the main AI classifier, the workflow sends the normalized feedback text through a guardrails node. This is intended to detect PII, jailbreak attempts, and secret-key-like content.
+
+## 1.3 AI Classification & Drafting
+
+If the input is allowed, an AI Agent classifies the feedback and drafts a suggested response. The prompt forces the model to return JSON only, with a fixed list of fields and allowed categories.
+
+## 1.4 Output Validation & Guardrails
+
+The workflow validates the AI output using a Code node that parses JSON, checks required fields, and enforces allowed enum values. The drafted response is then screened with a second guardrails node.
+
+## 1.5 Confidence Gate & Routing
+
+Only outputs that are both valid and above a confidence threshold are routed automatically. Routing is category-based:
+- `bug_report` / product issue path -> engineering
+- `feature_request` -> product backlog
+- `complaint` -> customer success escalation
+- `praise` -> marketing/testimonial flag
+
+Items with low confidence or validation failures are sent to a human-review queue.
+
+---
+
+# 2. Block-by-Block Analysis
+
+## 2.1 Intake & Normalize
+
+### Overview
+
+This block receives external feedback data, maps inconsistent input fields into a normalized structure, and checks whether the required fields are present. It acts as the entry gate for the workflow and ensures downstream nodes receive predictable data.
+
+### Nodes Involved
+
+- Webhook - Feedback Intake
+- Normalize Feedback
+- Has Required Fields?
+- Respond - Missing Fields
+
+### Node Details
+
+#### Webhook - Feedback Intake
+
+- **Type and technical role:** `n8n-nodes-base.webhook`; workflow entry point for HTTP POST requests.
+- **Configuration choices:**
+ - Path: `customer-feedback`
+ - HTTP Method: `POST`
+ - Response Mode: `responseNode`, meaning the workflow expects dedicated Respond to Webhook nodes later.
+- **Key expressions or variables used:** None in the node itself.
+- **Input and output connections:**
+ - No input; it is the trigger.
+ - Outputs to `Normalize Feedback`.
+- **Version-specific requirements:** Type version `2`.
+- **Edge cases or potential failure types:**
+ - Wrong HTTP method.
+ - Incorrect payload format.
+ - Missing `body` object in the inbound request, which would affect normalization logic.
+ - If no response node is reached, the webhook request may hang or fail.
+- **Sub-workflow reference:** None.
+
+#### Normalize Feedback
+
+- **Type and technical role:** `n8n-nodes-base.code`; standardizes raw input into a canonical feedback object.
+- **Configuration choices:**
+ - Reads from `$input.first().json.body`.
+ - Builds:
+ - `feedbackId` as `FB-` + current timestamp
+ - `customerName` from `name` or `fullName`, default `Anonymous`
+ - `customerEmail` from `email`
+ - `feedbackText` from `feedback`, `message`, or `body`
+ - `product` from `product` or `service`, default `general`
+ - `source` default `web`
+ - `receivedAt` as current ISO timestamp
+ - Adds boolean `hasRequiredFields`.
+- **Key expressions or variables used:**
+ - `$input.first().json.body`
+ - JavaScript normalization logic
+- **Input and output connections:**
+ - Input from `Webhook - Feedback Intake`
+ - Output to `Has Required Fields?`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - If `json.body` is undefined, `raw.name` access will throw.
+ - Non-string values for fields may break `.trim()` unless coerced.
+ - Timestamp-based `feedbackId` is simple but not collision-proof in high-throughput parallel calls.
+- **Sub-workflow reference:** None.
+
+#### Has Required Fields?
+
+- **Type and technical role:** `n8n-nodes-base.if`; checks that required normalized fields are present.
+- **Configuration choices:**
+ - Condition checks whether `$json.hasRequiredFields` is `true`.
+ - This means only email and feedback text are mandatory.
+- **Key expressions or variables used:**
+ - `={{ $json.hasRequiredFields }}`
+- **Input and output connections:**
+ - Input from `Normalize Feedback`
+ - True output to `Input Guardrails`
+ - False output to `Respond - Missing Fields`
+- **Version-specific requirements:** Type version `2.2`.
+- **Edge cases or potential failure types:**
+ - If upstream code fails before setting `hasRequiredFields`, this condition may evaluate unexpectedly.
+ - Business rule mismatch: response says “Email and feedback text are required” while `customerName` and `product` are optional.
+- **Sub-workflow reference:** None.
+
+#### Respond - Missing Fields
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`; returns a 400 error for incomplete input.
+- **Configuration choices:**
+ - Response code: `400`
+ - Response format: JSON
+ - Body returns:
+ - `status: error`
+ - `message: Email and feedback text are required`
+- **Key expressions or variables used:**
+ - JSON body built with expression syntax.
+- **Input and output connections:**
+ - Input from false branch of `Has Required Fields?`
+ - No downstream output
+- **Version-specific requirements:** Type version `1.1`.
+- **Edge cases or potential failure types:**
+ - If reached after partial data corruption, still returns generic missing-field message.
+- **Sub-workflow reference:** None.
+
+---
+
+## 2.2 Input Guardrails
+
+### Overview
+
+This block evaluates inbound feedback text before it reaches the classifier. It is designed to reduce unsafe or manipulative inputs by detecting PII, prompt-injection/jailbreak patterns, and secret-like content.
+
+### Nodes Involved
+
+- Input Guardrails
+- Input Guardrails LLM
+- Respond - Input Blocked
+
+### Node Details
+
+#### Input Guardrails
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.guardrails`; policy screening layer for the user-provided feedback text.
+- **Configuration choices:**
+ - Text to inspect: `{{$json.feedbackText}}`
+ - Enabled checks:
+ - PII: all
+ - Jailbreak threshold: `0.8`
+ - Secret key detection: permissiveness `balanced`
+- **Key expressions or variables used:**
+ - `={{ $json.feedbackText }}`
+- **Input and output connections:**
+ - Input from `Has Required Fields?` true branch
+ - Main output 0 to `AI - Classify + Draft`
+ - Main output 1 to `Respond - Input Blocked`
+ - AI language model input from `Input Guardrails LLM`
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:**
+ - Credential/model failure on the attached LLM.
+ - False positives on PII or jailbreak detection.
+ - Empty or extremely short feedback text may produce unstable screening behavior.
+- **Sub-workflow reference:** None.
+
+#### Input Guardrails LLM
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.lmChatOpenAi`; language model backend used by the input guardrails node.
+- **Configuration choices:**
+ - Model: `gpt-4o`
+ - Uses OpenAI credentials named `OpenAi account 2`
+- **Key expressions or variables used:** None.
+- **Input and output connections:**
+ - No main input
+ - Connected via `ai_languageModel` to `Input Guardrails`
+- **Version-specific requirements:** Type version `1.2`.
+- **Edge cases or potential failure types:**
+ - Invalid or missing OpenAI API credentials.
+ - Model availability, quota exhaustion, or rate limiting.
+- **Sub-workflow reference:** None.
+
+#### Respond - Input Blocked
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`; returns a blocked-input response when guardrails reject the message.
+- **Configuration choices:**
+ - Response code: `400`
+ - JSON body:
+ - `status: input_blocked`
+ - `message: Your message could not be processed. Please rephrase and try again.`
+- **Key expressions or variables used:** Static JSON expression.
+- **Input and output connections:**
+ - Input from second output of `Input Guardrails`
+ - No downstream output
+- **Version-specific requirements:** Type version `1.1`.
+- **Edge cases or potential failure types:**
+ - The message is generic and does not indicate which guardrail triggered.
+- **Sub-workflow reference:** None.
+
+---
+
+## 2.3 AI Classification & Drafting
+
+### Overview
+
+This block sends the normalized, guardrail-approved feedback to an AI Agent that classifies the feedback and drafts a suggested response. The prompt requests strict JSON and constrains category values.
+
+### Nodes Involved
+
+- AI - Classify + Draft
+- OpenRouter Chat Model1
+
+### Node Details
+
+#### AI - Classify + Draft
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.agent`; LLM-driven classification and response-drafting node.
+- **Configuration choices:**
+ - Prompt type: `define`
+ - Prompt includes:
+ - Customer name
+ - Product
+ - Feedback text
+ - Requires output fields:
+ - `sentiment`
+ - `category`
+ - `priority`
+ - `suggestedResponse`
+ - `confidence`
+ - Allowed categories in prompt:
+ - `bug_report`
+ - `feature_request`
+ - `praise`
+ - `complaint`
+ - `question`
+- **Key expressions or variables used:**
+ - `{{ $('Has Required Fields?').item.json.customerName }}`
+ - `{{ $('Has Required Fields?').item.json.product }}`
+ - `{{ $('Has Required Fields?').item.json.feedbackText }}`
+- **Input and output connections:**
+ - Input from `Input Guardrails`
+ - Main output to `Validate AI Output`
+ - AI language model input from `OpenRouter Chat Model1`
+- **Version-specific requirements:** Type version `1.7`.
+- **Edge cases or potential failure types:**
+ - Model may return non-JSON or fenced JSON despite instructions.
+ - Model may emit unsupported enum values.
+ - `confidence` is requested in the prompt but not required by validation logic; this can later affect routing.
+ - Prompt/category mismatch with downstream switch logic.
+- **Sub-workflow reference:** None.
+
+#### OpenRouter Chat Model1
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.lmChatOpenRouter`; model backend for the classifier agent.
+- **Configuration choices:**
+ - Model: `google/gemini-3-flash-preview`
+ - Uses OpenRouter credentials named `OpenRouter account 2`
+- **Key expressions or variables used:** None.
+- **Input and output connections:**
+ - No main input
+ - Connected via `ai_languageModel` to `AI - Classify + Draft`
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:**
+ - Invalid OpenRouter credentials.
+ - Model changes or preview-model instability.
+ - Rate limits or provider-side timeout.
+- **Sub-workflow reference:** None.
+
+---
+
+## 2.4 Output Validation
+
+### Overview
+
+This block parses and validates the AI response deterministically, then applies output guardrails to the drafted response text. It prevents malformed or policy-unsafe output from being routed automatically.
+
+### Nodes Involved
+
+- Validate AI Output
+- Output Guardrails
+- OpenRouter Chat Model
+- Respond - Output Blocked
+
+### Node Details
+
+#### Validate AI Output
+
+- **Type and technical role:** `n8n-nodes-base.code`; deterministic parser and schema validator for the AI result.
+- **Configuration choices:**
+ - Runs once for each item.
+ - Reads `input.output`.
+ - If output is a string:
+ - strips Markdown code fences
+ - parses JSON
+ - Requires fields:
+ - `sentiment`
+ - `category`
+ - `priority`
+ - `suggestedResponse`
+ - Valid enum values:
+ - Sentiment: `positive`, `negative`, `neutral`, `mixed`
+ - Category: `bug_report`, `feature_request`, `praise`, `complaint`, `question`
+ - Priority: `critical`, `high`, `medium`, `low`
+ - Returns `validationPassed: true/false`
+ - Adds `validationError` on failure
+- **Key expressions or variables used:**
+ - `$json`
+ - `input.output`
+- **Input and output connections:**
+ - Input from `AI - Classify + Draft`
+ - Output to `Output Guardrails`
+- **Version-specific requirements:** Code node type version `2`, mode `runOnceForEachItem`.
+- **Edge cases or potential failure types:**
+ - If the agent output format changes and `output` no longer exists, validation may fail or parse the wrong structure.
+ - `confidence` is not validated as required even though downstream logic expects it.
+ - No numeric range check for confidence.
+ - Invalid JSON is converted to a structured validation failure rather than hard-stopping.
+- **Sub-workflow reference:** None.
+
+#### Output Guardrails
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.guardrails`; screens the drafted response text before it is returned or used for routing.
+- **Configuration choices:**
+ - Text to inspect: `{{$json.suggestedResponse}}`
+ - Enabled checks:
+ - NSFW threshold: `0.8`
+ - Secret key detection: permissiveness `balanced`
+- **Key expressions or variables used:**
+ - `={{ $json.suggestedResponse }}`
+- **Input and output connections:**
+ - Input from `Validate AI Output`
+ - Main output 0 to `High Confidence + Valid?`
+ - Main output 1 to `Respond - Output Blocked`
+ - AI language model input from `OpenRouter Chat Model`
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:**
+ - If `suggestedResponse` is missing because validation already failed, guardrails behavior may depend on node implementation.
+ - False positives can block harmless outputs.
+- **Sub-workflow reference:** None.
+
+#### OpenRouter Chat Model
+
+- **Type and technical role:** `@n8n/n8n-nodes-langchain.lmChatOpenRouter`; model backend for output guardrails.
+- **Configuration choices:**
+ - Model: `google/gemini-3-flash-preview`
+ - Uses OpenRouter credentials named `OpenRouter account 2`
+- **Key expressions or variables used:** None.
+- **Input and output connections:**
+ - No main input
+ - Connected via `ai_languageModel` to `Output Guardrails`
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:**
+ - Credential, quota, or provider errors.
+- **Sub-workflow reference:** None.
+
+#### Respond - Output Blocked
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`; returns an error when output guardrails reject the generated response.
+- **Configuration choices:**
+ - Response code: `400`
+ - JSON body:
+ - `status: input_blocked`
+ - `message: Your message could not be processed. Please rephrase and try again.`
+- **Key expressions or variables used:** Static JSON expression.
+- **Input and output connections:**
+ - Input from second output of `Output Guardrails`
+ - No downstream output
+- **Version-specific requirements:** Type version `1.1`.
+- **Edge cases or potential failure types:**
+ - Status label says `input_blocked` even though this is an output-stage rejection.
+- **Sub-workflow reference:** None.
+
+---
+
+## 2.5 Confidence Gate & Routing
+
+### Overview
+
+This block decides whether the AI result is trusted enough to route automatically. Approved outputs are routed by category to placeholder actions, while low-confidence or invalid items are queued for human review.
+
+### Nodes Involved
+
+- High Confidence + Valid?
+- Route by Type
+- Create Jira Ticket
+- Add to Backlog
+- Escalate to CS
+- Flag as Testimonial
+- Queue for Human Review
+- Respond - Result
+
+### Node Details
+
+#### High Confidence + Valid?
+
+- **Type and technical role:** `n8n-nodes-base.if`; automation gate based on confidence and validation status.
+- **Configuration choices:**
+ - Checks:
+ - `confidence >= 0.7`
+ - `validationPassed = true`
+- **Key expressions or variables used:**
+ - `={{ $('Validate AI Output').item.json.confidence }}`
+ - `={{ $('Validate AI Output').item.json.validationPassed }}`
+- **Input and output connections:**
+ - Input from `Output Guardrails`
+ - True output to `Route by Type`
+ - False output to `Queue for Human Review`
+- **Version-specific requirements:** Type version `2.2`.
+- **Edge cases or potential failure types:**
+ - If `confidence` is missing or not numeric, the numeric comparison may fail or route unexpectedly.
+ - Validation failure still proceeds to this node; false branch handles it.
+- **Sub-workflow reference:** None.
+
+#### Route by Type
+
+- **Type and technical role:** `n8n-nodes-base.switch`; category-based deterministic routing.
+- **Configuration choices:**
+ - Uses category from `Validate AI Output`
+ - Named outputs:
+ - `Product Issue` when category equals `product_issue`
+ - `Feature Request` when category equals `feature_request`
+ - `Complaint` when category equals `complaint`
+ - `Praise` when category equals `praise`
+ - Fallback output enabled as output index 2
+- **Key expressions or variables used:**
+ - `={{ $('Validate AI Output').item.json.category }}`
+- **Input and output connections:**
+ - Input from `High Confidence + Valid?`
+ - Output 0 -> `Create Jira Ticket`
+ - Output 1 -> `Add to Backlog`
+ - Output 2 -> `Escalate to CS`
+ - Output 3 -> `Flag as Testimonial`
+- **Version-specific requirements:** Type version `3.2`.
+- **Edge cases or potential failure types:**
+ - Major logic mismatch: validator allows `bug_report`, but this switch expects `product_issue`.
+ - Because fallback output is set to index 2, unmatched categories such as `bug_report` and `question` will go to the `Complaint` branch (`Escalate to CS`), which is likely unintended.
+ - `question` has no explicit route.
+- **Sub-workflow reference:** None.
+
+#### Create Jira Ticket
+
+- **Type and technical role:** `n8n-nodes-base.code`; placeholder for engineering ticket creation.
+- **Configuration choices:**
+ - Returns original validated payload plus:
+ - `action: jira_ticket_created`
+ - `destination: engineering`
+- **Key expressions or variables used:**
+ - `$('Validate AI Output').first().json`
+- **Input and output connections:**
+ - Input from `Route by Type`
+ - Output to `Respond - Result`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - Currently no real Jira integration.
+ - Uses `.first()` from `Validate AI Output`, which is acceptable in single-item execution but can be problematic if batching changes later.
+- **Sub-workflow reference:** None.
+
+#### Add to Backlog
+
+- **Type and technical role:** `n8n-nodes-base.code`; placeholder for product backlog routing.
+- **Configuration choices:**
+ - Returns original validated payload plus:
+ - `action: added_to_backlog`
+ - `destination: product`
+- **Key expressions or variables used:**
+ - `$('Validate AI Output').first().json`
+- **Input and output connections:**
+ - Input from `Route by Type`
+ - Output to `Respond - Result`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - Placeholder only; no actual tracker integration.
+- **Sub-workflow reference:** None.
+
+#### Escalate to CS
+
+- **Type and technical role:** `n8n-nodes-base.code`; placeholder for customer success escalation.
+- **Configuration choices:**
+ - Returns original validated payload plus:
+ - `action: escalated_to_cs`
+ - `destination: customer_success`
+ - `priority: high`
+- **Key expressions or variables used:**
+ - `$('Validate AI Output').first().json`
+- **Input and output connections:**
+ - Input from `Route by Type`
+ - Output to `Respond - Result`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - Overwrites the AI-generated priority with `high`.
+ - Because of switch fallback behavior, unmatched categories may land here unintentionally.
+- **Sub-workflow reference:** None.
+
+#### Flag as Testimonial
+
+- **Type and technical role:** `n8n-nodes-base.code`; placeholder for marketing routing of praise.
+- **Configuration choices:**
+ - Returns original validated payload plus:
+ - `action: testimonial_candidate`
+ - `destination: marketing`
+- **Key expressions or variables used:**
+ - `$('Validate AI Output').first().json`
+- **Input and output connections:**
+ - Input from `Route by Type`
+ - Output to `Respond - Result`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - Placeholder only; no CRM/marketing integration.
+- **Sub-workflow reference:** None.
+
+#### Queue for Human Review
+
+- **Type and technical role:** `n8n-nodes-base.code`; fallback path for low-confidence or invalid AI outputs.
+- **Configuration choices:**
+ - Returns original validated payload plus:
+ - `action: needs_human_review`
+ - `reason: low_confidence_or_validation_failure`
+- **Key expressions or variables used:**
+ - `$('Validate AI Output').first().json`
+- **Input and output connections:**
+ - Input from false branch of `High Confidence + Valid?`
+ - Output to `Respond - Result`
+- **Version-specific requirements:** Code node type version `2`.
+- **Edge cases or potential failure types:**
+ - No actual queue system is connected yet.
+ - Reason is generic; does not distinguish confidence failure from schema failure.
+- **Sub-workflow reference:** None.
+
+#### Respond - Result
+
+- **Type and technical role:** `n8n-nodes-base.respondToWebhook`; final success/fallback response to the caller.
+- **Configuration choices:**
+ - Responds with JSON body containing:
+ - `feedbackId`
+ - `category`
+ - `action`
+ - `destination`
+ - `confidence`
+- **Key expressions or variables used:**
+ - `{{ JSON.stringify({ feedbackId: $json.feedbackId, category: $json.category, action: $json.action, destination: $json.destination, confidence: $json.confidence }) }}`
+- **Input and output connections:**
+ - Inputs from:
+ - `Create Jira Ticket`
+ - `Add to Backlog`
+ - `Escalate to CS`
+ - `Flag as Testimonial`
+ - `Queue for Human Review`
+- **Version-specific requirements:** Type version `1.1`.
+- **Edge cases or potential failure types:**
+ - If upstream action nodes omit `destination`, response may contain undefined values.
+ - Human review path does not set `destination`, so it may be absent in the final JSON.
+- **Sub-workflow reference:** None.
+
+---
+
+## 2.6 Documentation / Visual Annotation Nodes
+
+### Overview
+
+These nodes do not affect execution. They provide in-canvas explanations and setup guidance.
+
+### Nodes Involved
+
+- Sticky Note
+- Sticky Note1
+- Sticky Note2
+- Sticky Note3
+- Sticky Note4
+- Sticky Note5
+
+### Node Details
+
+#### Sticky Note
+
+- **Type and technical role:** `n8n-nodes-base.stickyNote`; canvas documentation.
+- **Configuration choices:** Contains a full overview of the pipeline, setup instructions, and customization notes.
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+#### Sticky Note1
+
+- **Type and technical role:** Sticky note; labels the intake section.
+- **Configuration choices:** Content `## Intake & Normalize`
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+#### Sticky Note2
+
+- **Type and technical role:** Sticky note; labels input guardrails section.
+- **Configuration choices:** Content `## Input Guardrails`
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+#### Sticky Note3
+
+- **Type and technical role:** Sticky note; labels AI classification section.
+- **Configuration choices:** Content `## AI Classification & Drafting`
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+#### Sticky Note4
+
+- **Type and technical role:** Sticky note; labels output validation section.
+- **Configuration choices:** Content `## Output Validation`
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+#### Sticky Note5
+
+- **Type and technical role:** Sticky note; labels routing section.
+- **Configuration choices:** Content `## Route by Type`
+- **Input and output connections:** None.
+- **Version-specific requirements:** Type version `1`.
+- **Edge cases or potential failure types:** None.
+- **Sub-workflow reference:** None.
+
+---
+
+# 3. Summary Table
+
+| Node Name | Node Type | Functional Role | Input Node(s) | Output Node(s) | Sticky Note |
+|---|---|---|---|---|---|
+| Webhook - Feedback Intake | Webhook | Receives POSTed customer feedback | | Normalize Feedback | ## Intake & Normalize |
+| Normalize Feedback | Code | Normalizes inbound payload into a standard schema | Webhook - Feedback Intake | Has Required Fields? | ## Intake & Normalize |
+| Has Required Fields? | IF | Checks whether email and feedback text exist | Normalize Feedback | Input Guardrails; Respond - Missing Fields | ## Intake & Normalize |
+| Respond - Missing Fields | Respond to Webhook | Returns 400 for incomplete payloads | Has Required Fields? | | ## Intake & Normalize |
+| Input Guardrails | Guardrails | Screens feedback text for PII, jailbreak, and secret-like content | Has Required Fields? | AI - Classify + Draft; Respond - Input Blocked | ## Input Guardrails |
+| Input Guardrails LLM | OpenAI Chat Model | Supplies LLM backend for input guardrails | | Input Guardrails | ## Input Guardrails |
+| Respond - Input Blocked | Respond to Webhook | Returns 400 when inbound text is blocked by guardrails | Input Guardrails | | ## Input Guardrails |
+| AI - Classify + Draft | LangChain Agent | Classifies feedback and drafts a response | Input Guardrails | Validate AI Output | ## AI Classification & Drafting |
+| OpenRouter Chat Model1 | OpenRouter Chat Model | Supplies Gemini model backend for classification agent | | AI - Classify + Draft | ## AI Classification & Drafting |
+| Validate AI Output | Code | Parses and validates AI JSON output | AI - Classify + Draft | Output Guardrails | ## Output Validation |
+| Output Guardrails | Guardrails | Screens suggested response text before routing | Validate AI Output | High Confidence + Valid?; Respond - Output Blocked | ## Output Validation |
+| OpenRouter Chat Model | OpenRouter Chat Model | Supplies Gemini model backend for output guardrails | | Output Guardrails | ## Output Validation |
+| Respond - Output Blocked | Respond to Webhook | Returns 400 when generated output is blocked | Output Guardrails | | ## Output Validation |
+| High Confidence + Valid? | IF | Allows auto-routing only when validation passes and confidence is high enough | Output Guardrails | Route by Type; Queue for Human Review | ## Route by Type |
+| Route by Type | Switch | Routes approved items by feedback category | High Confidence + Valid? | Create Jira Ticket; Add to Backlog; Escalate to CS; Flag as Testimonial | ## Route by Type |
+| Create Jira Ticket | Code | Placeholder action for engineering routing | Route by Type | Respond - Result | ## Route by Type |
+| Add to Backlog | Code | Placeholder action for product backlog routing | Route by Type | Respond - Result | ## Route by Type |
+| Escalate to CS | Code | Placeholder action for customer success escalation | Route by Type | Respond - Result | ## Route by Type |
+| Flag as Testimonial | Code | Placeholder action for marketing/testimonial routing | Route by Type | Respond - Result | ## Route by Type |
+| Queue for Human Review | Code | Placeholder fallback for low-confidence or invalid results | High Confidence + Valid? | Respond - Result | ## Route by Type |
+| Respond - Result | Respond to Webhook | Returns final routing outcome to caller | Create Jira Ticket; Add to Backlog; Escalate to CS; Flag as Testimonial; Queue for Human Review | | ## Route by Type |
+| Sticky Note | Sticky Note | General in-canvas documentation | | | ## Complete Customer Feedback Pipeline
### How it works
This workflow chains all five stages of the hybrid deterministic + AI pattern:
1. **Intake** (Deterministic): Webhook receives feedback, Code node normalizes it, IF node validates required fields.
2. **Input Guardrails** (Deterministic): Guardrails node checks for PII, injection attempts, and blocked keywords. Flagged inputs are blocked.
3. **AI Classification + Drafting** (AI): AI Agent classifies the feedback (product issue, feature request, complaint, praise) and drafts a personalized response.
4. **Output Validation** (Deterministic): Code node validates the classification. Guardrails node checks the draft for policy violations.
5. **Routing** (Deterministic): High-confidence, valid results route by type: product issues create Jira tickets, feature requests go to backlog, complaints escalate to CS, and praise is flagged for testimonials. Low-confidence results queue for human review.
### Setup
- Connect your **LLM credentials** to the Chat Model nodes (AI Agent + both Guardrails nodes)
- Copy the Webhook test URL and send sample feedback with varying tone and content
- Review the **Route by Type** Switch node to map categories to your actual team integrations
### Customization
- Replace placeholder Code nodes (Jira, Backlog, Escalate, Testimonial) with real integrations
- Tune confidence thresholds in the **High Confidence + Valid?** node as you validate accuracy
- Add more feedback categories by extending the AI prompt and the Route by Type switch |
+| Sticky Note1 | Sticky Note | Section label | | | ## Intake & Normalize |
+| Sticky Note2 | Sticky Note | Section label | | | ## Input Guardrails |
+| Sticky Note3 | Sticky Note | Section label | | | ## AI Classification & Drafting |
+| Sticky Note4 | Sticky Note | Section label | | | ## Output Validation |
+| Sticky Note5 | Sticky Note | Section label | | | ## Route by Type |
+
+---
+
+# 4. Reproducing the Workflow from Scratch
+
+1. **Create a new workflow** in n8n and give it the title:
+ `Classify and route customer feedback with GPT-4o, Gemini, and guardrails`.
+
+2. **Add a Webhook node** named `Webhook - Feedback Intake`.
+ - Set **HTTP Method** to `POST`.
+ - Set **Path** to `customer-feedback`.
+ - Set **Response Mode** to `Using Respond to Webhook Node` / `responseNode`.
+
+3. **Add a Code node** named `Normalize Feedback` after the webhook.
+ - Connect `Webhook - Feedback Intake -> Normalize Feedback`.
+ - Paste logic that:
+ - reads `body` from the incoming webhook payload
+ - creates:
+ - `feedbackId`
+ - `customerName`
+ - `customerEmail`
+ - `feedbackText`
+ - `product`
+ - `source`
+ - `receivedAt`
+ - creates `hasRequiredFields`
+ - Use this logic behavior:
+ - `name` or `fullName` -> customer name
+ - `email` -> customer email
+ - `feedback`, `message`, or `body` -> feedback text
+ - `product` or `service` -> product
+ - defaults: `Anonymous`, `general`, `web`
+
+4. **Add an IF node** named `Has Required Fields?`.
+ - Connect `Normalize Feedback -> Has Required Fields?`.
+ - Configure a boolean condition:
+ - left value: `{{$json.hasRequiredFields}}`
+ - operation: `is true`
+
+5. **Add a Respond to Webhook node** named `Respond - Missing Fields`.
+ - Connect the **false** output of `Has Required Fields?` to it.
+ - Set **Response Code** to `400`.
+ - Set response format to JSON.
+ - Return:
+ - `status: error`
+ - `message: Email and feedback text are required`
+
+6. **Add a Guardrails node** named `Input Guardrails`.
+ - Connect the **true** output of `Has Required Fields?` to it.
+ - Set the text field to `{{$json.feedbackText}}`.
+ - Enable:
+ - PII detection: all
+ - Jailbreak detection threshold: `0.8`
+ - Secret keys detection: `balanced`
+
+7. **Add an OpenAI Chat Model node** named `Input Guardrails LLM`.
+ - Connect it to the `Input Guardrails` node using the AI language model connection.
+ - Select model `gpt-4o`.
+ - Configure valid OpenAI credentials.
+
+8. **Add a Respond to Webhook node** named `Respond - Input Blocked`.
+ - Connect the blocked/rejected output of `Input Guardrails` to it.
+ - Set **Response Code** to `400`.
+ - Return JSON:
+ - `status: input_blocked`
+ - `message: Your message could not be processed. Please rephrase and try again.`
+
+9. **Add a LangChain Agent node** named `AI - Classify + Draft`.
+ - Connect the allowed output of `Input Guardrails` to it.
+ - Set prompt mode to define text manually.
+ - Use a prompt equivalent to:
+ - include customer name, product, and feedback text
+ - instruct the model to return only valid JSON
+ - require these fields:
+ - `sentiment`
+ - `category`
+ - `priority`
+ - `suggestedResponse`
+ - `confidence`
+ - restrict categories to:
+ - `bug_report`
+ - `feature_request`
+ - `praise`
+ - `complaint`
+ - `question`
+
+10. **Add an OpenRouter Chat Model node** named `OpenRouter Chat Model1`.
+ - Connect it as the AI language model for `AI - Classify + Draft`.
+ - Set model to `google/gemini-3-flash-preview`.
+ - Configure valid OpenRouter credentials.
+
+11. **Add a Code node** named `Validate AI Output`.
+ - Connect `AI - Classify + Draft -> Validate AI Output`.
+ - Set execution mode to **Run Once for Each Item**.
+ - Implement logic that:
+ - reads the agent result from `output`
+ - strips code fences if present
+ - parses JSON
+ - verifies required fields:
+ - `sentiment`
+ - `category`
+ - `priority`
+ - `suggestedResponse`
+ - verifies enum values:
+ - sentiment in `positive`, `negative`, `neutral`, `mixed`
+ - category in `bug_report`, `feature_request`, `praise`, `complaint`, `question`
+ - priority in `critical`, `high`, `medium`, `low`
+ - returns `validationPassed: true` or `false`
+ - returns `validationError` on failure
+
+12. **Add a Guardrails node** named `Output Guardrails`.
+ - Connect `Validate AI Output -> Output Guardrails`.
+ - Set text to `{{$json.suggestedResponse}}`.
+ - Enable:
+ - NSFW threshold: `0.8`
+ - Secret keys detection: `balanced`
+
+13. **Add an OpenRouter Chat Model node** named `OpenRouter Chat Model`.
+ - Connect it as the AI language model for `Output Guardrails`.
+ - Use model `google/gemini-3-flash-preview`.
+ - Reuse the same OpenRouter credentials if desired.
+
+14. **Add a Respond to Webhook node** named `Respond - Output Blocked`.
+ - Connect the blocked/rejected output of `Output Guardrails` to it.
+ - Set **Response Code** to `400`.
+ - Return the same generic blocked message as above.
+
+15. **Add an IF node** named `High Confidence + Valid?`.
+ - Connect the allowed output of `Output Guardrails` to it.
+ - Add two AND conditions:
+ - `{{$('Validate AI Output').item.json.confidence}} >= 0.7`
+ - `{{$('Validate AI Output').item.json.validationPassed}} is true`
+
+16. **Add a Switch node** named `Route by Type`.
+ - Connect the true output of `High Confidence + Valid?` to it.
+ - Create explicit outputs for category checks based on `{{$('Validate AI Output').item.json.category}}`.
+ - In the original workflow, the rules are:
+ - `product_issue`
+ - `feature_request`
+ - `complaint`
+ - `praise`
+ - Important: this is inconsistent with the AI prompt and validator, which use `bug_report`, not `product_issue`.
+ - To reproduce exactly, keep the mismatch.
+ - To improve it, replace `product_issue` with `bug_report`.
+ - Also add an explicit route for `question` if you want correct handling.
+
+17. **Set fallback behavior** on `Route by Type`.
+ - The original workflow enables fallback output and points unmatched categories to output index 2, which effectively sends fallback items to the complaint/customer-success branch.
+ - Reproduce this only if you want exact parity.
+ - Recommended fix: route fallback to human review instead.
+
+18. **Add a Code node** named `Create Jira Ticket`.
+ - Connect the first switch output to it.
+ - Return the validated payload plus:
+ - `action: jira_ticket_created`
+ - `destination: engineering`
+ - In a production build, replace this with a real Jira node and ticket mapping.
+
+19. **Add a Code node** named `Add to Backlog`.
+ - Connect the feature-request output to it.
+ - Return the validated payload plus:
+ - `action: added_to_backlog`
+ - `destination: product`
+
+20. **Add a Code node** named `Escalate to CS`.
+ - Connect the complaint output to it.
+ - Return the validated payload plus:
+ - `action: escalated_to_cs`
+ - `destination: customer_success`
+ - `priority: high`
+
+21. **Add a Code node** named `Flag as Testimonial`.
+ - Connect the praise output to it.
+ - Return the validated payload plus:
+ - `action: testimonial_candidate`
+ - `destination: marketing`
+
+22. **Add a Code node** named `Queue for Human Review`.
+ - Connect the false output of `High Confidence + Valid?` to it.
+ - Return the validated payload plus:
+ - `action: needs_human_review`
+ - `reason: low_confidence_or_validation_failure`
+
+23. **Add a Respond to Webhook node** named `Respond - Result`.
+ - Connect all final action nodes to it:
+ - `Create Jira Ticket`
+ - `Add to Backlog`
+ - `Escalate to CS`
+ - `Flag as Testimonial`
+ - `Queue for Human Review`
+ - Configure JSON response returning:
+ - `feedbackId`
+ - `category`
+ - `action`
+ - `destination`
+ - `confidence`
+
+24. **Add sticky notes** if you want the same visual organization.
+ - Add one overall note describing the five-stage pipeline and setup guidance.
+ - Add section labels:
+ - `## Intake & Normalize`
+ - `## Input Guardrails`
+ - `## AI Classification & Drafting`
+ - `## Output Validation`
+ - `## Route by Type`
+
+25. **Configure credentials**
+ - **OpenAI credential**
+ - Required by `Input Guardrails LLM`
+ - Must support `gpt-4o`
+ - **OpenRouter credential**
+ - Required by `OpenRouter Chat Model1`
+ - Required by `OpenRouter Chat Model`
+ - Must allow `google/gemini-3-flash-preview`
+
+26. **Test with sample POST payloads** such as:
+ - valid bug report
+ - feature request
+ - praise/testimonial candidate
+ - complaint
+ - incomplete payload missing email
+ - content likely to trigger guardrails
+
+27. **Recommended fixes after reproduction**
+ - Align `bug_report` vs `product_issue`
+ - Validate `confidence` explicitly as required numeric output
+ - Add a defined route for `question`
+ - Send fallback switch results to human review instead of complaint escalation
+ - Replace placeholder Code nodes with Jira, Linear, Slack, Zendesk, email, or CRM integrations
+
+### Sub-workflow setup
+
+This workflow does **not** invoke any sub-workflows and has only one entry point:
+- `Webhook - Feedback Intake`
+
+---
+
+# 5. General Notes & Resources
+
+| Note Content | Context or Link |
+|---|---|
+| Complete Customer Feedback Pipeline: intake, input guardrails, AI classification and drafting, output validation, and routing | In-canvas project note |
+| Connect your LLM credentials to the Chat Model nodes for the AI Agent and both Guardrails nodes | Setup guidance |
+| Copy the Webhook test URL and send sample feedback with varying tone and content | Testing guidance |
+| Review the Route by Type Switch node to map categories to your actual team integrations | Customization guidance |
+| Replace placeholder Code nodes for Jira, backlog, escalation, and testimonial routing with real integrations | Production hardening |
+| Tune confidence thresholds in the High Confidence + Valid? node as you validate accuracy | Model calibration |
+| Add more feedback categories by extending the AI prompt and the Route by Type switch | Functional extension |
+
+## Important implementation notes
+
+- The workflow title and visual notes describe routing for “product issues,” but the classifier and validator use `bug_report`. This mismatch is the most important logic issue in the current design.
+- The `Route by Type` fallback configuration sends unmatched categories to the complaint branch, which can misroute `bug_report` and `question`.
+- The final blocked-output response currently uses `status: input_blocked`, which is semantically misleading for output-stage failures.
+- `confidence` is used for routing but is not enforced by the validator; this should be tightened if the workflow is used in production.
\ No newline at end of file