Project 06 / Demonstration build
Support requests sorted by AI, approved by a person
Every email in the shared inbox is classified, matched to HubSpot, and posted to Slack with a drafted reply where a standard answer exists. A person approves, edits, reassigns, or archives each one.
Category: Support triage · Designed outcomes, not measured client results.
Who this is for
Teams with a shared support or information inbox where a person reads and forwards each request by hand.
The problem
Example client brief
“Everything comes to one inbox: sales questions, support, spam, existing clients, vendors. Someone reads each one and forwards it. We are slow, and the person doing it is our most expensive employee.”
- Request arrives in the shared inbox
- Classified, customer looked up in HubSpot, reply drafted
- Person approves, edits, or reassigns in Slack
Designed outcome
Clear requests get a fast, correct reply. Ambiguous ones get a person. Nothing goes out unreviewed.
What the workflow does
- A Gmail filter labels everything sent to the support address. n8n picks up each new message once, keyed on its Gmail message ID, and keeps only the new text from replies.
- The sender is looked up in HubSpot. A known contact brings their company, lifecycle stage, and deals onto the card. If HubSpot is down, triage carries on and the card says the lookup failed.
- Claude classifies the message as sales, support, existing client, vendor, spam, or unclear, with a confidence score and a one-line reason. A reply is read together with the earlier messages on its thread.
- Plain code, not the model, decides what happens next. Unclear messages, anything below the confidence threshold, a negative reply on an open thread, a thread that changes category, and existing clients go to a person. Vendor pitches and spam are marked for archive.
- Sales and support messages that pass get a drafted reply built only from approved standard answers. If the answer would need facts those don't contain, the draft is kept but the card goes to a person.
- The Slack card has Approve, Edit, Reassign, and Archive. Approve and Edit send the reply from Gmail on the same thread. Reassign mentions the teammate and labels the thread, and it refuses a bot or app. Each click is checked against Slack's signature, and a second click on a handled card sends nothing.
Tests and traces
The service has 46 tests. Ten canned emails run through the real graph with Claude and HubSpot scripted, so routing, the threshold, and thread handling are checked without a model. The same ten run against the real model with an evaluation script.
Before recording, I tried five vendor pitches dressed up as support requests, RFPs, and demo requests. The model called every one of them a vendor pitch and named the pitch in its reason. So the demo uses a lead seller who writes like a buyer, where the model hedges and the threshold decides.
LangSmith shows the inputs, the CRM record, the category, the reason, the gate, and the draft for every message. It shows what the model returned, not its private reasoning. All test data is fictional.
Failure handling
When it breaks
An angry reply arrives on a thread that was classified as sales.
The workflow knows it is a reply. It sends the earlier messages and their categories to the model, and a negative reply on an open thread goes to a person with the history attached instead of getting a cheerful sales draft.
A message could honestly be two things, like a lead seller who writes like a buyer.
The model says so in its reason and its confidence drops. Anything below the threshold skips the draft and goes to a person. The threshold is one number in n8n, and every card links to the LangSmith trace that shows why.
The Claude API is unavailable.
Nothing is guessed and nothing is sent. The message is queued, one alert posts to the alerts channel, and n8n retries every 10 minutes for an hour. When it goes through, the card appears and a recovered note posts.