DEV Community

anusha
anusha

Posted on

I Built an AI Subscription Cancel-Save Agent That Does Not Manipulate You

Cancel-save flows are easy to over-engineer into manipulative dark patterns, and easy to under-engineer into "your subscription has been cancelled, goodbye" experiences that lose revenue. A good cancel-save agent does three things: classifies the reason, offers one relevant save option, and respects a direct cancellation request.

The Telnyx code example is:

https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-subscription-cancel-save-retention-agent-python

It is a Python Flask app that combines Telnyx Voice, AI Inference, and Messaging into a small demo you can clone and run in a few minutes.

The Flow

A customer calls in saying they want to cancel. The Flask app receives the webhook, verifies the customer by caller ID, and asks why they want to cancel. AI Inference returns the reason (too_expensive, not_using, missing_feature, support_issue, competitor_switch, temporary_pause, other) and the sentiment. Hardcoded urgent phrases (lawyer, sue, fraud, chargeback) and direct cancel phrases (cancel now, please cancel my subscription) short-circuit before any offer.

Otherwise, the agent offers one save option from the OFFER_POLICY mapping. A "yes" applies the save or pauses the subscription. A "no" or "cancel" ends the call with a graceful cancellation. An ambiguous yes/no gets exactly one clarifying prompt — the agent never loops.

The case is recorded with the outcome (saved, cancelled, paused, transferred, needs_followup), and the customer record is updated. Hangups before resolution are recorded as needs_followup so reps can call back.

Why I Like This Example

It is small enough to demo live, but it covers the parts that often get cut from a "Hello World" voice AI demo:

  • Single-prompt AI Inference classification that returns structured JSON
  • An offer policy that maps reason to one offer and one default outcome
  • Hardcoded overrides for urgent phrases and direct cancel so the agent does not rely on the LLM to get safety right
  • One clarifying prompt on ambiguous yes/no so the conversation does not loop
  • Idempotent webhook handling so retries do not double-log cases
  • Hangup tracking so open cases never disappear

The design choice I care most about is non-manipulation. The agent does not push back on a direct cancellation, and it offers one save option — not five — when the customer is open to one. That maps to how good customer-success teams actually operate.

Run It Locally

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/ai-subscription-cancel-save-retention-agent-python
cp .env.example .env
pip install -r requirements.txt
python app.py
Enter fullscreen mode Exit fullscreen mode

Expose the webhook:

ngrok http 5000
Enter fullscreen mode Exit fullscreen mode

In the Telnyx Portal, set the Voice API app webhook URL to https://<ngrok-id>.ngrok-free.app/webhooks/voice. Seed a customer with POST /customers, then call the Telnyx number from the customer's phone.

What To Demo

I would keep the demo short and show three flows back-to-back:

  1. Customer says "I want to cancel, it's too expensive" then accepts the 25% off offer. Case ends saved, customer status active.
  2. Customer says "I want to cancel, it's too expensive" then declines. Case ends cancelled, customer status cancelled.
  3. Customer says "I want a lawyer this is fraud." Case ends transferred, call moves to the human escalation number.

That gives viewers the full loop without getting lost in implementation details.

Where To Take It Next

The demo uses in-memory state, which is fine for learning. Production would wire the customer and case stores into your billing system (Stripe, Recurly, Chargebee) so a saved outcome actually applies the discount and a paused outcome defers the next invoice. Add consent recording (record_channels: "dual") for compliance audits, and replace the caller-ID match with a one-time code flow for real verification.

Resources

Top comments (0)