# 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

```bash
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`.

```bash
curl "https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/save-offer?reason=too_much" \
  -H "Authorization: Bearer $USUAL_KEY"
```

```json
{
  "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

```bash
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:

```bash
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:

```bash
curl "https://regular-subs.fly.dev/api/v1/subscriptions/sub_123/portal-link?view=cancel" \
  -H "Authorization: Bearer $USUAL_KEY"
```

```json
{
  "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.
