A corporate gifting company in Northern California sells custom gift boxes to other businesses: client thank-yous, employee gifts, holiday programs, referral gifts. Two senior account executives handle outreach and every inbound reply. Behind them, a small team designs proposals, builds samples, and ships orders.
The reps' inboxes are busy in a particular way. Cold campaigns go out on Tuesdays, Wednesdays, and Thursdays from a separate sales engagement tool. Each wave brings back a wave of replies. Some are real interest. Some are sample requests. Some say "take me off your list." A lot are out-of-office auto-replies. Mixed in with all of that are the live client threads that actually pay the bills.
Before this project, one person triaged both inboxes by hand. Read every reply, decide what it is, draft a response, then create a task card on the team's Microsoft Planner board so the proposal and assembly people knew what to do next.
What was breaking
- Triage was the bottleneck. Every reply waited for the same person to get to it, and that person had other jobs.
- The board drifted from the inbox. A client would answer a question by email and the Planner card wouldn't catch up until someone remembered to edit it.
- Out-of-office replies were noise with something valuable inside. Many of them name a colleague to contact instead. Nobody had time to act on that, so the lead just sat there.
- Opt-outs went unhandled. One prospect replied to a cold email with the single word "REMOVE" and it sat in the inbox.
- Nobody was watching the order portals. Recurring accounts place orders through hosted forms. Whether an order had come in depended on someone thinking to look.
What we built
An agent that runs inside the company's Microsoft 365 tenant through the Microsoft Graph API. Before it read a single email, we locked it down. An Exchange application access policy limits the app to the two rep mailboxes. It cannot read anyone else's mail, and we confirmed that by testing against a third mailbox and getting denied.
Once an hour during business hours it does six things.
- Reads and classifies new replies in both inboxes: sample request, question, positive reply with no project yet, opt-out, out-of-office, or a message in an active client thread.
- Drafts a reply in the thread in the reps' voice and saves it as a draft. It never sends. Both reps share one house voice, so the agent writes one way. A style guard runs on every draft before it's saved. It strips em dashes, flags AI-sounding words, and flags more than one exclamation point.
- Creates or updates Planner cards. Every card the agent creates starts with "AI - " so the team can see at a glance where it came from. Cards carry context from the whole thread, an in-hand date at the top, and for samples the complete ship-to address, because the assembly team ships from the card alone. Before creating anything it looks for an existing card for that contact or company and updates it instead.
- Turns out-of-office replies into introductions. When an auto-reply to a cold campaign names a specific person as the backup contact, the agent writes a fresh first-touch intro to that person from the same rep's inbox, mentions who referred it, and includes the physical address and unsubscribe line the law requires. Then it files the auto-reply into that rep's six-month follow-up folder. It only does this for named individuals. Generic HR and payroll aliases are skipped, and an auto-reply inside an active client thread is left alone so a live conversation never gets buried.
- Handles opt-outs. A reply to a cold campaign that leads with "remove me," "unsubscribe," or "take me off" gets a short, polite confirmation and is filed. We tested the detector against false positives before turning it on. "Can you remove the champagne glasses?" does not trigger it.
- Checks the order portals. We connected the agent to the client's portal API, 57 forms across their recurring accounts, so "have any orders come through?" gets a real count, with recipient, ship-to, and card-message details for each submission.
The posture: drafts for people, actions for the board
The most important design decision was what the agent is allowed to do without asking.
Planner card actions are free. Creating, moving, reassigning, re-dating, closing, refreshing notes. These are internal and reversible, so the agent does them on its own judgment.
Any email to a client is a draft. A human reads it and sends it, or edits it, or deletes it. Two narrow exceptions have standing approval: the out-of-office introductions and the opt-out confirmations. Both are template-driven, both were reviewed once, and neither involves a price or a promise.
The reasons are practical. The reps' voice is the product. A single off-key email can cost a relationship that took months of outreach to open. And the knowledge that makes a reply useful, pricing, product specs, what a repeat client ordered last year, still lives in the team's heads. Until that's captured, the agent's job is to get a good draft in front of the human quickly.
How it runs day to day
The agent lives in a small cloud function that fires hourly. The timer itself runs in UTC, and the business-hours gate (Pacific time, Sunday through Friday for drafting, weekdays for the out-of-office job) is enforced in code. More on why below.
Four times a day, at 5am, 8am, 1pm, and 4pm Pacific, a scheduled review runs and produces a report. Each report lists what the agent did on the board and every email it's holding, numbered. The reviewer reads the report and replies with something like "send 2 and 5, hold 3, make 4 shorter." That's the whole review workflow. Nobody opens two inboxes and reads everything anymore.
Every miss gets a row in a miss log with a failure class: a rule that wasn't consulted, a negative that wasn't verified, a fix that overfit one case, a wrong target, and so on. When the same class shows up twice, the last fix was a patch and the class needs a real fix.
All the operating rules live in one playbook file. We learned that the hard way too. For a while there were two copies, they drifted, and the drift caused real bugs twice. Now there is one file and the codebase links to it.
What changed
The clearest result came on the first day. When we connected the portal API, it immediately surfaced an order that had been submitted in April and never caught. The team believed the portal had no submissions. The client had even been told there were none. No fulfillment task existed. The order was confirmed and shipped, and a daily sweep of every portal is now on the build list so it can't happen again.
Beyond that:
- Opt-outs are handled within the hour instead of sitting in the inbox.
- Out-of-office replies turn into new first-touch conversations instead of getting deleted.
- The Planner board matches the inbox, because the same process that reads the email updates the card.
- The person who used to triage two inboxes now reads four short reports a day and approves by number.
We haven't published hours-saved numbers for this one because the drafting side is still in supervised mode while the voice rules get refined. The remaining work is in those rules, and they live in a file that gets a little better with every review.
The gotchas
- Cloud timers ignore your time zone setting on Linux. Our schedule was written in Pacific hours and ran in UTC, which meant the agent worked from 1am to 10am. Fire hourly and gate the hours in code.
- Outlook strips paragraph spacing from reply drafts. It injects a zero-margin style on paragraphs, so a neatly formatted draft arrives as a wall of text. Inline margins on every paragraph fix it.
- Trust the folder, not the run summary. An audit of the follow-up folders found that 5 of 6 introductions the agent had correctly identified during the build week were never sent. The run summary looked clean. Check the sent folder yourself, especially right after a code change.
- Names in auto-replies are messy. Display names arrive as "Last, First" and sometimes carry an employee ID in parentheses. One early intro went out reading "I reached out to Last, First." Now the agent normalizes the name, and if it can't produce a clean one it uses the first name only or drops the reference entirely. A missing name beats a garbled one.
- Two copies of anything will drift. Two signature constants, two playbooks, two code paths that both claim to be canonical. Pick one.
What this means for you
If you have a sales inbox, a support inbox, or a shared mailbox where one person is the triage bottleneck, this shape works: an agent that reads, classifies, drafts, and keeps your task board current, with every client-facing message held for a human. It fits any business with a task tool that has an API and a mailbox you're willing to scope access to.
The draft-only posture is the part to copy. We wrote up the reasoning in what an AI email assistant actually does for a small sales team. If you want to talk through your inbox specifically, book a free 30-minute audit. We'll map where the replies come from, what an agent could handle, and what should stay with a person.