Blog / Product Brand Operations
Sort a Customer Service Inbox in Gmail (and the One Spot an LLM Helps)
By Keyona Meeks, Founder, ReRev Labs · Oct 31, 2026 · 10 min read
The short answer: Most customer service mail falls into a few piles: where is my order, returns and refunds, wholesale questions, and the rest. Plain keyword rules can put a label on the first three. A short Apps Script does that every 15 minutes. It only adds labels. It never replies, archives or deletes. Whatever matches no rule goes into an Unsorted pile. That pile is the one place I would let an LLM suggest a label, and a person decides. I tested the script with a mock in Node. Nothing was run in Google or in a real inbox.
By the end of this post, you'll be able to:
- Pick the few labels a small support inbox needs.
- Run a script that labels new threads by keyword.
- Handle the Unsorted pile without letting an LLM act alone.
- Know what the rules will get wrong.
We sell operations builds for product brands, so weigh this post with that in mind. This is the plain version. A shared inbox tool may suit you better if several people answer mail all day.
Which labels does a small support inbox need?
Start with three plus a catch-all. These are my starting set, not a standard:
- CS/Order status: where is my order, tracking questions, late arrivals.
- CS/Returns: returns, refunds, exchanges, damaged items.
- CS/Wholesale: stockists, bulk orders, retailers.
- CS/Unsorted: everything that matches no rule.
Every label starts with CS/. I believe Gmail shows a slash in a label name as a nested label, but I could not confirm that from Google's pages. The script uses that prefix to know which threads it has already handled.
Change the names and add labels to fit your business. Keep the list short. Five labels people use beat fifteen they ignore.
How do the rules work?
Each label has a list of words. The script lowercases the subject and the first 1,500 characters of the first message. It checks the rules in order, and the first label with a matching word wins. A message that matches nothing gets CS/Unsorted.
That order matters. A message that says "my order has not arrived and I want a refund" matches both Returns and Order status. In the script, Returns comes first, so it wins. Put the label you most want to see first at the top.
What does the script look like?
Paste this into an Apps Script project signed in to the Gmail account that holds the inbox. Nothing in it runs until you call it.
// Sorts new inbox threads into labels by keyword rules. It only adds labels. It never replies, archives or deletes.
var MAX_THREADS = 50; // Most threads to look at per run.
var PREFIX = 'CS/'; // Every label this script adds starts with this.
var UNSORTED = 'CS/Unsorted';
var RULES = [
{ label: 'CS/Returns', words: ['return', 'refund', 'exchange', 'damaged', 'wrong size'] },
{ label: 'CS/Order status', words: ['where is my order', 'tracking', 'has not arrived', 'not arrived', 'shipping status'] },
{ label: 'CS/Wholesale', words: ['wholesale', 'stockist', 'bulk order', 'retailer'] }
];
function pickLabel(text) {
var t = String(text || '').toLowerCase();
for (var i = 0; i < RULES.length; i++) {
for (var j = 0; j < RULES[i].words.length; j++) {
if (t.indexOf(RULES[i].words[j]) !== -1) return RULES[i].label;
}
}
return UNSORTED;
}
function getOrCreateLabel(name) {
return GmailApp.getUserLabelByName(name) || GmailApp.createLabel(name);
}
function hasOurLabel(thread) {
return thread.getLabels().some(function (l) { return l.getName().indexOf(PREFIX) === 0; });
}
function sortInbox() {
var threads = GmailApp.search('in:inbox', 0, MAX_THREADS);
var counts = {};
threads.forEach(function (thread) {
if (hasOurLabel(thread)) return;
var first = thread.getMessages()[0];
var text = thread.getFirstMessageSubject() + ' ' + String(first.getPlainBody()).slice(0, 1500);
var name = pickLabel(text);
thread.addLabel(getOrCreateLabel(name));
counts[name] = (counts[name] || 0) + 1;
});
console.log(JSON.stringify(counts));
return counts;
}
function installTrigger() {
ScriptApp.newTrigger('sortInbox').timeBased().everyMinutes(15).create();
}
What it does, with Google's description of each call as I read it in the method tables this session:
GmailApp.search(query, start, max): "Search Gmail with the given query." The script asks forin:inbox, up to 50 threads. I could not confirm Gmail's search syntax from a Google page this session, so test that query on your account.thread.getLabels(): "Returns the user-created labels on this thread."getName(): "Gets the name of this label." The script skips any thread that already has aCS/label.thread.getFirstMessageSubject(): "Gets the subject of the first message in the thread."getMessages(): "Gets the messages in this thread."getPlainBody(): "Gets the content of the body of this message without HTML formatting."GmailApp.getUserLabelByName(name): "Retrieves a label given the label name."createLabel(name): "Create a new user label of the given name."thread.addLabel(label): "Adds this label to the thread."
For the trigger, everyMinutes(n) "Specifies to run the trigger every n minutes," and n "must be 1, 5, 10, 15 or 30." I read that page as excerpts. Run installTrigger once, then check your project's triggers list for exactly one entry.
What permission does it need?
It reads message text and adds labels, so Google will ask you to allow access to your Gmail. Per an earlier review of Google's authorization page, scopes are "determined automatically by scanning the script code." I could not confirm the exact permission names this session. Read the prompt before you approve it. The script sends no email and does not leave your account.
Because it reads customer messages, think about who has access to the script project. Do not share the project with people who should not read the inbox.
How do you test it?
- Make a Gmail test inbox, or use a quiet one. Send yourself five messages: one about an order, one about a refund, one about wholesale, one with no keyword and one with two keywords.
- Run
sortInboxby hand. Open each message and check its label. - Run it again. Nothing should change, because labeled threads are skipped.
- Check that the script did not archive, delete or mark anything. It should not.
- Try your real subject lines. Read what ends up in Unsorted.
- Only then run
installTrigger.
What did the test show?
I tested the script in Node with a mock of Gmail. All 18 checks passed. They covered each label, case-insensitive matching, the first rule winning, an already-labeled thread being skipped, a thread with only a personal label still being sorted, labels created once and reused, an empty inbox, an empty message body, a second run changing nothing, the trigger chain and the absence of any reply, archive or delete call in the source. The mock does not show how Gmail returns threads, so run the test above in a real account first.
Where does an LLM fit in this?
The only spot is the Unsorted pile. Those messages matched no rule, so a person would have to read them anyway. An LLM can read each one and suggest a label, such as "this looks like a wholesale question," or suggest a new keyword to add to a rule. A person checks the suggestion and applies the label. I would not let an LLM reply to customers or move messages by itself, because it can misread a message and sound sure of it.
Customer mail holds names, addresses and order details. Check your own privacy rules before you paste any of it into an LLM tool, and strip what you can. I do not give legal advice. My stance: AI writes the code, plain automation runs it, and an LLM shows up only where the input is messy and a person checks the result. For the longer argument, see AI agents vs. automation.
What will the rules get wrong?
- Words with two meanings. "Return" can mean a return request or "return to the site." Read the Unsorted and Returns piles for a week and tune the list.
- Order. The first match wins. Re-order if the wrong label keeps winning.
- Long threads. Only the first message is read. A thread that changes topic keeps its first label.
- Spam and newsletters. They will land in Unsorted. Add a rule or filter them another way.
- The 50-thread limit. Each run looks at the 50 newest inbox threads. If more than 50 recent threads already carry a
CS/label, older unlabeled ones are not reached. RaiseMAX_THREADSif you need to, and test the run time.
I could not confirm Gmail's built-in filter menus from Google's pages this session, so I give no click path for them. The script does not need them.
Where this has limits
- Nothing was run in Google or in a real inbox. The test was a mock in Node.
- The search query is untested. I could not confirm Gmail's search syntax from a Google page.
- Keywords are blunt. They will mislabel some mail.
- It labels only. A person reads and answers every message.
- It reads customer mail. Control who can open the script project.
- Execution limits apply. I could not confirm Google's current script limits for your account. Per an earlier review, runtime is limited per run, so keep
MAX_THREADSmodest. - No ReRev prices and no promises about results. I have no data on time saved.
If you want help turning this into a system that sorts the inbox and tracks what gets answered, see our AI operations service or tell us what lands in your inbox. Do not send customer emails, passwords or login details.
Frequently asked questions
Can Gmail sort a customer service inbox automatically?
Yes, with labels. Gmail has filters, and an Apps Script can apply labels by rule. This post uses a script.
Does the script reply to customers?
No. It only adds labels. A person answers every message.
Where does an LLM help?
On the messages that match no rule. It suggests a label, and a person decides.
Will it label old mail?
It looks at the 50 newest inbox threads per run and skips threads that already have a CS/ label.
Is it safe to paste customer mail into an LLM?
That depends on your privacy rules and the tool. Check them first and remove personal details.
Do I need a shared inbox tool instead?
If many people answer mail all day, a shared inbox tool may fit better. This script is for a small team.
Read next: Where an LLM actually belongs in a workflow explains the one-spot rule this inbox uses and four more spots that earn it.