Handle a cancel request
A customer writes in: "please cancel my subscription". A support agent (human or AI) can offer one thing that might help, and cancel straight away if it doesn't. Never more than one offer, and never making cancelling harder: that's what customers expect, and what laws like California's require.
1. Find the subscription
curl "https://regular-subs.fly.dev/api/v1/subscriptions?status=ACTIVE&customerId=gid://shopify/Customer/7208" \
-H "Authorization: Bearer $USUAL_KEY"Look customers up by their Shopify id: support tools and Shopify both have it. (We don't store customers' emails, so you can't search by one.)
2. Ask why, then get the offer
The reason decides the offer. Map what the customer said to one of: too_much, too_expensive, taking_break, not_needed, didnt_like, other.
curl "https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/save-offer?reason=too_much" \
-H "Authorization: Bearer $USUAL_KEY"{
"offer": {
"kind": "less_often",
"every": 2,
"interval": "MONTH",
"heading": "Get it every 2 months instead?",
"detail": "Same items, less often. Change it back anytime.",
"accept": "Switch"
}
}offer is null when nothing is worth offering. The store chooses which offers are allowed; a discount only appears if the store turned it on, and at most once a year per subscription.
3a. They take it
curl -X POST https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/save \
-H "Authorization: Bearer $USUAL_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "too_much", "offer": "less_often"}'Only the offer that was shown can be accepted. For a swap, add the variantId they chose from offer.choices. The save is counted in the store's retention report.
3b. They don't
Cancel right away, with the reason:
curl -X POST https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/cancel \
-H "Authorization: Bearer $USUAL_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "too_much", "comment": "Moving abroad"}'The customer gets a cancellation confirmation email (if the store has it on), and nothing is charged again, including retries of a failed payment.
Or let them do it themselves
If the customer would rather choose for themselves, or you'd rather not wait for an approval, send them a link straight to the cancel step in the store's customer portal:
curl "https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/portal-link?view=cancel" \
-H "Authorization: Bearer $USUAL_KEY"{
"url": "https://your-store.com/apps/usual/portal?subscription=sub_123&view=cancel",
"note": "Safe to send to the customer. They log in to the store first, and only see their own subscriptions."
}They see the same single offer for their reason, then can cancel in one click. Leave out view for a link to the subscription itself, where they can skip, reschedule, pause or update their card. The link holds no secret, so it's fine in an email or chat.
With an AI agent
Through MCP this is find_subscriptions, get_save_offer, then plan_change with subscriptions.accept_save_offer or subscriptions.cancel, or get_portal_link to hand it to the customer. The get_save_offer tool tells the agent the rules: one offer, in its own words, and cancel if they say no.
With an agent key, both taking an offer and cancelling wait for someone at the store to approve them in Usual โ Activity, because they're medium and high risk. The agent can tell the customer it's been passed on and confirm once it's done.