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:
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.
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_
, 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 s 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
, d
, transferred
, needs_followup
), and the customer record is updated. Hangups before resolution are recorded as needs_followup
so reps can call back.
It is small enough to demo live, but it covers the parts that often get cut from a "Hello World" voice AI demo:
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.
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
Expose the webhook:
ngrok http 5000
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.
I would keep the demo short and show three flows back-to-back:
saved
, customer status active
.cancelled
, customer status cancelled
.transferred
, call moves to the human escalation number.That gives viewers the full loop without getting lost in implementation details.
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 d
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.