Back to blog
ai

How I'd Put a Subscription Store's Customer Support on Autopilot

By Youcef EL KAMEL
10 min read

How I'd Put a Subscription Store's Customer Support on Autopilot

After the last two articles, you know I like to start from a concrete problem.

This time I’m not starting from a question I fumbled in an interview. I’m starting from a job I’d take tomorrow morning.

Picture a subscription store. Thousands of customers paying every month. And a support inbox that’s drowning.

Most people answer that with “let’s hire two more agents.” I look at the invoice line instead.

Because subscription support is 80% tickets that come back every single day. “How do I cancel?” “My payment failed.” “Where’s my order?” Those tickets don’t need intelligence. They need consistency. And consistency is exactly what a well-constrained AI agent does better than a tired human at 6pm.

Here’s how I’d approach it. Not in theory. As an operator.

Week 1: learn the floor before writing code

The first mistake would be opening an editor on Monday morning.

For one week, I write no code. I tag the real tickets.

Every ticket that comes in gets a label: intent, channel, time to resolution, escalated or not. By hand if I have to. It’s tedious. It’s the best investment in the whole project.

Because after seven days, you’re not looking at a wall of tickets anymore. You’re looking at a distribution.

And that distribution always says the same thing: 5 to 7 intents make up 80% of the volume.

On a subscription store, those intents are almost always:

  • subscription cancellation
  • failed payment
  • order status / delivery
  • refund request
  • plan or frequency change
  • “where’s my access / my content?”
  • payment method update

Seven intents. That’s your target.

I’m not building an agent that “answers everything.” I’m building an agent that crushes those seven, perfectly, and hands off the rest.

That’s the first operator decision: you don’t want 100% coverage, you want 80% of the volume absorbed without error.

Tag first, code second. Otherwise you automate your gut feeling, not your actual traffic.

The architecture: three data layers, policy in the code

A support agent that only knows the FAQ is useless. It answers “here’s our refund policy” when the customer wants to know where their own refund is.

The difference between a chatbot and a useful agent is data access. Three layers of it.

Layer 1 — static

The FAQ, terms, policies, standard delivery windows. It doesn’t change from one customer to the next. It can live in a search index or a simple injected context.

The easiest layer. Also the least useful on its own.

Layer 2 — dynamic

The real status. Order #4821 has shipped. The subscription is active until August 3rd. The last payment failed yesterday.

The agent doesn’t “know” this layer. It fetches it through tools, exactly when it needs it.

Layer 3 — private

The customer account. Identity, history, payment methods, entitlements. The sensitive layer. The one where a mistake is expensive.

The agent never touches it freely. It goes through typed tools, with hard-coded rules.

And that’s where the most important architecture decision shows up.

Policy in the code, not in the prompt

A lot of people write their business rules into the system prompt. “Don’t refund past 30 days.” “Don’t cancel without confirming.”

That’s a mistake.

A prompt is a suggestion. A model can bypass it, forget it, or misread it under the right phrasing from an angry customer.

A hard rule in the code is a guarantee.

So I don’t put the policy in the prompt. I put it in the tool.

// The business rule lives HERE, in the code.
// The model can only "ask" — it never decides on its own.
async function cancelSubscription(customerId: string, reason: string) {
  const sub = await billing.getSubscription(customerId);

  if (!sub || sub.status !== "active") {
    return { ok: false, message: "No active subscription to cancel." };
  }

  // Hard policy: ALWAYS offer a save offer first.
  // The model can't skip this step, even if the customer insists.
  if (!sub.saveOfferShown) {
    await billing.flagSaveOfferShown(customerId);
    return { ok: false, requiresSaveOffer: true, offer: pickSaveOffer(sub) };
  }

  await billing.scheduleCancellation(customerId, reason);
  return { ok: true, effectiveDate: sub.currentPeriodEnd };
}

Look at what this tool does.

The model asks for a cancellation. The code decides. No active subscription? Clean refusal. Save offer not shown yet? The tool refuses and returns the offer to present. The model can’t short-circuit the business rule, even if the customer writes “cancel everything, right now, I don’t care.”

That’s policy as code: risky decisions don’t depend on a model’s mood. They depend on a function you wrote, tested, and can prove.

The prompt handles tone and conversation. The code handles money and entitlements.

That split is the backbone of the whole system.

The subscription-specific plays

Subscription support isn’t generic support. There are three moments where the money is decided. I treat each as a dedicated flow.

The cancellation flow with save offers

Cancellation isn’t an ending. It’s a conversation.

When a customer wants to cancel, the agent doesn’t say “done, goodbye.” It listens to the reason. Too expensive? Not using it enough? Would a pause do it?

And based on the reason, it offers the right save offer:

  • a free month for the customer who finds it pricey
  • a 2-month pause for the one traveling
  • a downgrade to a cheaper plan for the one over-paying for premium

Every successful save offer is retained MRR. A €15/month customer kept for 6 more months is €90 that stays. Across thousands of customers, it becomes the most profitable line in the system.

The rule “always offer before canceling” lives in the code, not the prompt. The model chooses how to say it. The code guarantees that it gets said.

Failed payment recovery (dunning)

A failed payment is almost never a customer leaving. It’s an expired card. A hit limit. A pending transfer.

Without a system, that customer churns for nothing.

The agent handles dunning proactively:

  • it detects the failure through the dynamic layer
  • it reaches out with the right message, at the right time
  • it offers to update the payment method through a secure tool
  • it follows up on a cadence — no spam, no giving up

A recovered failed payment is money you’d already earned and were about to let slip. It’s often the play with the best immediate ROI, because you’re not convincing anyone: you’re fixing friction.

Refund policy as code

The refund is the most dangerous tool. So it’s the most locked down.

30-day window, prorated amount, cap, eligible cases: it’s all in the function. The agent never “decides” a refund. It checks eligibility through the code, and the code answers yes or no.

An out-of-window customer who insists? The tool refuses, and the case goes to human escalation. The agent can’t be manipulated into giving money, because it doesn’t have that power. The code does.

Measuring like an operator

A system without metrics is a belief. I run this kind of project on four numbers. No more.

1. Deflection rate. The share of tickets resolved without a human. The queen metric. It’s the one that turns into savings.

2. Save rate. On cancellation requests, the share of customers retained by a save offer. Defended MRR, ticket by ticket.

3. Time-to-resolution. The time between open and resolved. An agent that answers in 20 seconds, at 3am, changes the experience — and the CSAT.

4. CSAT. Satisfaction. The guardrail. If deflection climbs but CSAT drops, you’re not automating, you’re damaging.

And here’s the calculation that turns all of this into a business argument.

Take a support desk handling 8,000 tickets a month. A human ticket costs, say, €5 (agent time, tooling, oversight). That’s €40,000 a month in support cost.

If the agent deflects 70%, you’re not automating “text.” You’re pulling 5,600 tickets out of the human queue.

5,600 tickets × €5 = €28,000 in avoided invoice. Per month.

And I haven’t even counted the save rate yet. If the agent retains just 200 subscriptions a month at €15, that’s €3,000 of defended MRR, every month, compounding.

That’s operator thinking. You’re not selling “a chatbot.” You’re showing an invoice line going down and an MRR line holding steady.

The rollout: shadow mode, then progressive autopilot

I never plug an agent into full autonomy on day one. That would be ego, not engineering.

The rollout happens in three stages.

Stage 1 — shadow mode

The agent runs on real tickets, but talks to no one. It proposes its answers. A human compares. We measure the gap.

During this phase, every answer the agent would have gotten wrong becomes a test case. You’re not debugging in a vacuum. You’re building a test suite from real ground.

Stage 2 — supervised autopilot

The agent answers for real, but on the safest intents first. Order status. FAQ. Payment method updates. The human keeps the wheel on refunds and edge cases.

At every escalation, the same question: is this a genuine human case, or a hole in my tools? If it’s a hole, I fill it, and the escalation becomes one more test.

Stage 3 — expanded autopilot

Intent by intent, as the metrics hold, I widen the scope. The agent absorbs more and more volume. The human focuses on what actually deserves a human: the rare, sensitive, emotional cases.

This is the point that matters most, and I’ll repeat it because it always gets forgotten:

AI is an accelerator, not a replacement.

The agent doesn’t fire anyone. It frees humans from repetitive volume so they can handle the 20% of tickets that genuinely need judgment. The difficult customer, the moral call, the borderline complaint: those stay human. The “how do I cancel?” for the 400th time this week: that’s for the agent.

Every escalation that comes up isn’t a failure. It’s data. It becomes a test case, the system tightens, and the scope widens again.

What I take away

Putting subscription support on autopilot isn’t an AI project. It’s an operator project that uses AI.

The AI part is the easy part. The real work is:

  • tag the floor before coding
  • find the 5-7 intents that make up 80% of the volume
  • separate the three data layers
  • put the policy in the code, not the prompt
  • treat cancellation, dunning, and refunds as dedicated flows
  • measure by deflection, save rate, resolution, CSAT
  • roll out in shadow, then progressive autopilot

And above all: keep the human where they create value, and give them back the time stolen by repetition.

The model does the conversation. The code does the rules. You do the savings.


This kind of job — an agent wired into real code, thought through as an invoice line — is exactly what I do on freelance engagements. If your team has a support desk that’s drowning, we can look at it together in 30 minutes: calendly.com/yelkamel/30min.

#customer support #AI agents #subscriptions #deflection #save rate #dunning #policy as code #ROI #automation