An auto-reply system does not fail by sending wrong information. It fails by sending right information in a tone that reads as dismissive, or by auto-resolving a ticket that needed a human. Both failures are avoidable if you classify before you draft, and escalate before you guess. This page covers both gates.
Classify intent before drafting anything
Do not start from "what should the reply say." Start from "what kind of ticket is this," because intent determines both the tone you must use and the ceiling of what automation is allowed to resolve on its own.
| Intent | Tone required | Auto-resolvable ceiling |
|---|---|---|
| Order status ("where is my order") | Neutral, informative | Fully automatable β this is the easy 80% |
| Return / refund request | Neutral, procedural, still warm | Automatable if inside the documented policy window; otherwise escalate |
| Product complaint (item defective, wrong item, damaged) | Apologetic, solution-first | Automatable for standard remedies (replace/refund) within policy; escalate if the customer disputes the remedy |
| Angry / emotional message | Calm, specific, de-escalating β never templated boilerplate | Draft only β a human should review or send, even if the underlying request (e.g. a refund) is otherwise auto-resolvable |
Run this as a discrete first step, not a mental note β put "classify intent" in front of "draft reply" as its own function call with its own output. A ticket that says "this is the third time I'm writing and my package still hasn't shipped" is order-status content wrapped in angry-message intent. Classify on the emotional signal, not the surface topic β the emotional signal is what sets the tone ceiling.
Technical β signals to detect intent
Check these signals in priority order β higher signal wins if multiple are present:
- Angry/emotional markers β profanity, all-caps, exclamation-heavy phrasing, words like "furious," "unacceptable," "scam," "lawyer," "chargeback," "BBB." Any of these overrides the nominal topic.
- Explicit request type β keywords like "refund," "return," "damaged," "wrong item," "where is," "tracking," "hasn't arrived."
- Repeat-contact flag β whether this order ID or email has an existing open or recently-closed ticket. A second contact is a different intent category than a first, even with identical wording.
- Default β if nothing matches, hold to the conservative (return/refund) ceiling, not the permissive (order status) one.
Build templates with real variable slots, not generic text
A fully generic template β "Thank you for reaching out. We're looking into your order and will follow up soon." β reads as a form letter because it is a form letter, and customers correctly interpret that as "nobody read my message." The fix is not to write from scratch every time; it's to keep the template but force it to carry 1-2 pieces of real order data.
Business β why concrete beats generic here
A generic reply and a personalized template cost the same to send β both are automated β but they produce different downstream behavior. A customer who sees their real order number and ship date believes the system actually looked at their case, and is less likely to reply again asking "did you even read this." A customer who sees only "we're looking into it" assumes the opposite and re-contacts, turning one ticket into two β worse for the metric automation exists to protect (contacts-per-order) than a slightly slower reply would have been.
Technical β the minimum variable set per template
Every auto-reply template must pull at least the order number, and at least one of: ship date, carrier tracking number, delivery estimate, or refund amount, depending on intent. Pull these from the order record at send time β never let the model guess or approximate a date or number.
Order status template
----------------------
Hi {{customer_first_name}}, your order {{order_number}} shipped
{{ship_date}} via {{carrier}} and is tracking to arrive by
{{estimated_delivery_date}}. Track it here: {{tracking_url}}
Return/refund template (within policy window)
----------------------
Hi {{customer_first_name}}, I've started a return for order
{{order_number}} ({{item_name}}). Your refund of {{refund_amount}}
will post to your original payment method within {{refund_window}}
business days once the item is received at our facility.
Two slots are usually enough. Do not over-parameterize β a template with eight variables is fragile (one missing field breaks the whole send) and reads as robotic in a different way. Order number plus one contextual fact is the floor that separates "personalized" from "generic," and it's also the ceiling most support systems need for the easy 80%.
The four hard escalation triggers
Some tickets must never receive an auto-reply as the final action, regardless of how confidently the intent classifier or the draft model scored them. Wire these as a hard gate that runs after classification and before send β not as a suggestion the model can talk itself out of.
- Profanity, threat of chargeback, or legal/regulatory language. Any mention of "lawyer," "sue," "chargeback," "BBB," "FTC," "attorney general," or profanity directed at the company or a person. These are liability-adjacent and require a human's judgment on wording.
- Request falls outside the documented return/refund policy window. A return past the stated day limit, or a refund on a final-sale item. Auto-approving sets a precedent the business didn't choose; auto-denying without discretion loses a resolvable customer.
- Repeat contact on the same order. If this is the second or later message on the same order ID within a short window, the first automated attempt already failed. A second automated reply compounds the "nobody is listening" perception β escalate on the second touch, not the third.
- Explicit request for a human, or conflicting information (the order ID doesn't match any order on the account, or the customer disputes facts the system has on file). Automation should not adjudicate a factual dispute.
If a ticket trips any trigger, do not suppress the auto-reply pipeline silently β route it to a human queue with a handoff note (below). Silence reads worse than a slow reply.
Writing the escalation handoff note
Forwarding the raw ticket text to a human agent forces them to redo the triage work the system already did. A proper handoff note answers, in a few lines, what the human would otherwise have to reconstruct: what happened, why it didn't auto-resolve, and what's already true about the account.
Technical β required fields in a handoff note
ESCALATION β order #{{order_number}}
Trigger: repeat contact (2nd message in 4 days)
Customer sentiment: frustrated, not yet hostile
Intent: return/refund request
What's on file: order shipped {{ship_date}}, delivered {{delivery_date}}
per carrier tracking; customer reports item arrived damaged.
Policy check: within the {{return_window}}-day return window β eligible.
What the customer wants: full refund, not a replacement (stated explicitly).
What automation already tried: sent standard damaged-item reply on
first contact offering a replacement; customer replied rejecting
the replacement.
Suggested next step: approve refund directly β do not offer
replacement again.
This format β trigger, sentiment, intent, facts on file, policy check, stated preference, prior automated attempt, suggested next step β takes seconds to generate from data the system already has, and it turns a five-minute human investigation into a ten-second confirm-and-send.
Before/after β de-escalating an angry customer
Before (generic acknowledgment β what not to send):
Thank you for contacting us. We're sorry to hear about your experience. We are looking into your issue and will get back to you as soon as possible.
This tells the customer nothing was read. It doesn't reference their order, their specific complaint, or acknowledge the emotion in their message β so an already-angry customer reads it as a form letter and escalates further (often publicly).
After (specific, de-escalating):
Hi {{customer_first_name}}, I've read through your message about order
{{order_number}} and I understand why three delayed updates in a row would be
frustrating. Here's exactly where things stand: your item shipped {{ship_date}}
and the carrier is showing a delay, currently estimating arrival by
{{revised_delivery_date}}. I've already flagged this order for our support team
to review a refund of the shipping cost given the delay, and someone will
confirm that with you within {{sla_hours}} hours. You won't need to write in
again for this β we'll follow up on the same thread.
The rewrite works because it names the specific order, acknowledges the specific complaint ("three delayed updates"), states a concrete fact instead of "looking into it," and closes the loop on next steps. This version is a draft for human send, per the tone table above β angry-message intent stays in the draft-only lane even when the underlying facts are fully known and auto-resolvable in isolation.