AI email triage for shared inboxes, and the human check
Gmail's change notification does not contain the email. It contains an address and a history ID, and you go and fetch the rest. Almost every disappointing triage build starts by assuming the opposite.
Every company that grows past about ten people ends up with an address nobody owns. info@, support@, sales@, bookings@. Mail arrives, several people can see it, and the one thing that never reliably happens is a decision about who answers what. The backlog that builds up is not really a volume problem. It is an ownership problem wearing a volume problem's clothes.
AI triage is a sensible fix for that, and a much better fit than the chatbot projects it usually gets bundled with. Classifying a message and routing it is narrow and checkable. Writing the reply that goes out under your company's name is neither. The builds that work keep those two jobs apart, and most of this note is about where that line sits and what the mail platforms will actually give you to work with.
What triage means, narrowly
Triage here means five things and deliberately not a sixth. Read the incoming message, assign it a category, set a priority, route it to a person or a queue, and optionally draft a reply. The sixth thing, sending that reply without a person seeing it, is where the risk lives and it is not part of a first build. Keeping the send out of scope is what makes the rest of the project assessable, because every other step produces a decision somebody can look at and correct.
That framing matters more than the model choice. We have written before about how to use AI in operations without breaking the business, and the pattern is the same one: automate the classification, keep the commitment human.
What the mailbox actually hands you
Start at the API, because it sets the shape of everything above it. For Gmail you call watch on the mailbox with a Cloud Pub/Sub topic you own, and you can narrow it with labelIds and labelFilterBehavior so you only hear about the inbox. Then comes the part people do not expect. According to Google's own push notification guide, the notification payload carries the user's email address and a new mailbox history ID, and nothing else. There is no subject, no sender, no body. You take that history ID, call history.list against your last known one, and work out what changed yourself.
Two operational facts follow from the same page and both of them become line items. The watch must be renewed at least once every seven days or updates stop, and Google recommends calling it daily. That means a scheduled job whose only purpose is keeping the subscription alive, and if it fails you stop receiving mail notifications without any error arriving anywhere. The second fact is the rate ceiling: each watched Gmail user has a maximum notification rate of one event per second, and the service drops user notifications above that rate. Google also states plainly that notifications are usually delivered within seconds but can occasionally be delayed or dropped, and recommends falling back to periodic history.list calls after quiet periods. A reconcile pass is therefore not a refinement you add later. It is part of the first version.
Microsoft 365 shared mailboxes work differently enough to matter. Graph change notifications let you subscribe to a mail folder directly, for example the inbox under a given user, and Microsoft's change notification reference puts a hard ceiling on it: a maximum of 1,000 active subscriptions per mailbox across all applications. The same reference notes that when a resource limit is exceeded, the attempt to create a subscription fails with a 403 Forbidden, and the error message names the limit you hit. Worth reading before you design a subscription per label or per category.
| What you need | Gmail API | Microsoft Graph |
|---|---|---|
| Change signal | watch() on the mailbox, published to your Pub/Sub topic | A subscription on the mail folder, delivered to your webhook |
| What the payload contains | Email address and a new history ID, no message content | A notification referencing the resource that changed |
| Keeping it alive | Call watch at least every 7 days, daily recommended | Subscriptions expire and must be renewed |
| Ceiling to design around | One event per second per watched user, excess dropped | 1,000 active subscriptions per mailbox, all apps combined |
| Failure to plan for | Notifications occasionally delayed or dropped | Exceeding a resource quota returns 403 Forbidden |
The loop that catches people
Google's guide includes a warning that is easy to read past and expensive to rediscover: avoid triggering new mailbox changes from inside your notification handler, because that can create a notification loop. Now consider what a triage system does when it finishes thinking. It applies a label. Applying a label is a mailbox change. The mailbox change produces a notification. The notification runs the handler again.
The fix is not difficult, it is just work that nobody scoped: the handler has to recognise changes it caused itself and stop, and it has to be safe to run twice on the same message. Build that in on day one. Found in production, it presents as a mysterious cost spike and a mailbox full of duplicated labels, and it takes a week to unpick.
Where the model is good, and where it guesses
Language models are genuinely strong at the bounded questions. Is this a new enquiry or a reply to an existing thread. What language is it in. Does it mention an order or invoice reference. Which of our six categories does it belong to. Is the tone a complaint. These are classification problems over text you already hold, they have a correct answer, and you can measure them against a sample a person has labelled. This is the same territory as AI document processing, and where it still needs a person.
Urgency is where it starts guessing, and the reason is not a model weakness. Urgent in a customer's wording is not urgency in your business, and the two disagree constantly. A message reading "still waiting on this" is only urgent if the earlier thread shows it is the third time of asking, which means useful triage needs thread history and not merely the newest message. That is a data decision, not a prompt.
Some calls should not be automated at all in a first build, whatever the confidence score says:
- Anything touching money back: refunds, credit notes, discounts already promised.
- Anything with a legal or regulatory flavour, including data deletion requests.
- Cancelling, changing or expediting an order that is already in motion.
- Committing to a date. A model has no idea what your delivery schedule looks like this week.
- Quoting a price, which is the same trap we described in what to trust a support chatbot with.
Where the human check sits
The arrangement that survives contact with a real team is narrow: the system assigns and prioritises on its own, and a person approves anything that leaves the building. Drafting a first answer that somebody then edits is a good use of the model, and it is the same shape as what AI meeting notes are actually good at. The draft saves the blank page, the person keeps the judgement.
Log the overrides, visibly. Record what the classifier decided, how confident it was, and who changed it to what. That log is the only honest evidence about whether the thing works, it is the material you use to improve the categories, and it is the argument for widening the automation later or for leaving it exactly where it is. A triage system without an override log degrades and nobody can prove it.
What it costs to keep running
Three things cost money every month and only one of them is the model. There is the subscription renewal job, there is the reconcile pass that exists because notifications can be dropped, and there is per-message inference. In the builds we have done the inference is usually the small number and the always-on infrastructure is the predictable one. If the triage result has to land in a CRM rather than a label, the integration itself becomes the larger line item, which we costed out in API integration between two systems.
When not to build this
Three situations where the answer is no, and they are common. If the inbox takes fewer than roughly thirty messages a day, a labelling convention and one named owner per weekday will beat any build, at a cost of zero. If your categories are not stable yet, automating them encodes a decision you have not actually made, and you will be retraining against a moving target. And if nobody will own the override log, skip it: the system will quietly get worse and the first clear signal will be a customer complaint about something that was routed into a void.
Where it does earn its keep is the middling case. Enough volume that ownership genuinely fails, categories that have settled, and a team that would rather edit a draft than start one. Build the classifier, keep the send button human, renew the subscription on a schedule, reconcile on a timer, and read the override log monthly. That is a small system, and small is why it keeps working.
Describe it. We build it.
Seven or twelve days, pay on delivery, a year of maintenance included. Bring the problem, not a spec.
Book a meetingRead next
Construction project management software, scoped to the contract
Construction project management software for contractors, scoped around the money: the payment clock, variations, retention, and what belongs in phase one.
ReadA client portal for a professional services firm, scoped honestly
A client portal for professional services firms: the four things clients log in for, the access control failure that ends projects, and what to defer first.
Read