Claude Media
Stripeの解約理由をClaudeで分類し打ち手つき月次報告にする

Stripeの解約理由をClaudeで分類し打ち手つき月次報告にする

Stripeのcancellation_detailsは、システムが付ける理由と、顧客が選んだ理由コードと、自由記述の三層でできています。層ごとに扱いを分けてClaudeで再分類し、件数表と打ち手を月次報告にまとめる手順です。

Stripeでサブスクが解約されると、サブスクリプションの cancellation_details に理由が残ります。ところが、この欄には出どころの違う情報が混ざっています。理由コードだけを集計すると、解約の実態とずれた報告になりがちです。

先に層を分け、自由記述だけをClaudeで読み直し、件数表と打ち手を添えて月次報告にする。この記事はその流れを、取り出しから報告の体裁まで順に書きます。MRRや解約数の推移を引く側の話はClaudeとBaremetricsの連携、会計側はClaudeでStripeの経理業務を効率化する使い方にあります。ここで扱うのは、解約した人が「なぜ」を残した部分の定性分析です。

cancellation_detailsは三層でできている

Subscriptionオブジェクトの cancellation_details は、次の4つのフィールドを持ちます。公式のAPIリファレンスでは、いずれもnullableです。

フィールド中身値が入る条件
reason中身解約された理由(システム側の分類)値が入る条件解約の経路による
feedback中身顧客が選んだ理由コード値が入る条件顧客が明示的に解約した場合
feedback_option中身顧客向けにカスタマイズした選択肢値が入る条件顧客が明示的に解約した場合(expand で展開可)
comment中身顧客の自由記述値が入る条件顧客が明示的に解約した場合

reason が取る値は4つです。

  • cancellation_requested: APIまたはダッシュボードから明示的に解約された
  • payment_failed: 支払い失敗を受けて、請求設定に従い自動で解約された
  • payment_disputed: 支払いの異議申し立てを受けて、請求設定に従い自動で解約された
  • canceled_by_retention_policy: livemode: false のサブスクが、テストデータの保持ポリシーで自動的に期限切れになった

feedback が取る値は、too_expensive(料金が高い)、missing_features(機能が足りない)、switched_service(別のサービスを見つけた)、unused(もう必要ない)、customer_service(カスタマーサービスが期待以下)、low_quality(品質が期待以下)、too_complex(使うには複雑すぎた)、other(その他)の8つです。

この構造から、集計の前に決めておくことが見えてきます。payment_failed の解約には、顧客が理由を選ぶ場面がそもそもありません。いわゆる非自発の解約で、打ち手は商品改善ではなく、カード更新の案内や再請求の設定になります。理由コードの集計に混ぜると、「理由不明」が膨らんで見えます。

理由コードと自由記述はなぜずれるのか

顧客が解約理由を答える主な経路は、カスタマーポータルのキャンセルページです。管理者は、表示する理由を次の8つから選べます。

  • 料金が高い
  • より多くの機能が必要
  • 別の商品を見つけた
  • 商品が必要なくなった
  • カスタマーサービスが期待以下だった
  • 期待していたほど使い勝手が良くなかった
  • 期待していたよりも品質が劣っていた
  • その他の理由

自由記述は、顧客が「その他の理由」を選んだときに任意で入力できます。つまりポータル経由では、comment が入るのは feedback = other の行が中心です。ここにずれが生まれます。

  • other を選んだ人は、8つの選択肢のどれにも収まらないと感じた人です。自由記述の中身を読むと、実は料金の話や、特定機能の不足の話であることが少なくありません
  • 逆に too_expensive を選んだ人の本音が、実は「使いこなせず元が取れなかった」という利用不足だった、という行は自由記述が無いため見抜けません
  • APIやダッシュボードから担当者が解約した場合は、comment を担当者が書くことも、空のままにすることもあります。書き手が顧客でなく社内の人であるため、顧客の声と同じ重みでは数えられません

Stripe側の説明にあるとおり、顧客が選んだ理由はダッシュボードのBilling > サブスクリプションの詳細、Stripe Sigma、subscription.updated のWebhookで確認できます。Claudeに読ませるのは、このうち自由記述の入った行と、feedback_option を使っている場合はその文字列です。

会話

理由コードの集計だけでは足りない場面

  1. 後輩

    解約理由はfeedbackの集計で出ていますよね。月次報告はそれで足りませんか。

  2. 先輩

    足りない月があります。otherが全体の3割を超えたら、その3割の中身を読まないと打ち手が立ちません。自由記述はそこに集まっています。

  3. 後輩

    payment_failedの行は理由コードがないですよね。それも自由記述の対象ですか。

  4. 先輩

    いいえ。そこは顧客が理由を答えていないので、別の表にします。打ち手の宛先が、プロダクトではなく請求のリトライ設定になるからです。

手順1: 解約済みサブスクを取り出す

取り出し方は、Stripeダッシュボードからのエクスポート、StripeコネクタをClaudeにつなぐ方法、APIを直接叩く方法の3通りです。コネクタの接続手順はClaude Stripe連携の設定方法とできることにまとめています。月次の定例にするなら、読み取り専用のキーでAPIから取る形が再現しやすくなります。

サブスクリプションの一覧は、既定では解約されていないものだけが返ります。解約済みを取るには status=canceled を指定します。status=ended は、解約済みに加えて、初回支払いの不成立で期限切れになったものも含みます。理由を知りたい対象は前者なので、canceled を使います。

# 読み取り専用の制限付きキーを環境変数に入れておく
curl -s -G https://api.stripe.com/v1/subscriptions \
  -u "$STRIPE_RESTRICTED_KEY:" \
  -d status=canceled \
  -d limit=100 \
  -d "created[gte]=1759276800" \
  | jq '[.data[] | {
      id,
      canceled_at,
      ended_at,
      reason: .cancellation_details.reason,
      feedback: .cancellation_details.feedback,
      comment: .cancellation_details.comment
    }]' > canceled.json

上の例は、Stripeの GET /v1/subscriptions の仕様に沿って組んだ一例です。実行前に、自分の環境で次の点を確かめてください。

  • created[gte] は、サブスクの作成日を絞る条件です。解約日では絞れません。月初に解約された古いサブスクも取りたいので、作成日で広く取って、後から canceled_at で月を切るほうが安全です
  • 1回に返るのは最大100件です。それを超える月は、ページ送りの starting_after を使って続きを取ります
  • 顧客のメールアドレスや氏名は出力に含めていません。自由記述に顧客自身が連絡先を書いていることはあるため、次の節で伏せます

月の振り分けはcanceled_atとended_atのどちらか

cancel_at_period_end を使った解約では、canceled_at は解約操作をした時刻を指し、課金期間が終わってサブスクが実際に解約状態へ移る時刻ではありません。実際に終わった時刻は ended_at に入ります。

月次報告で「その月に解約を決めた人」を数えるなら canceled_at、「その月にMRRから外れた人」を数えるなら ended_at です。両方を同じ表に載せると数がずれて見えるので、報告の冒頭に「解約操作の月で数えている」と書いておきます。

手順2: 送る前に絞って伏せる

livemode: false のテスト用サブスクは、ポリシーで自動的に期限切れになり、reason が canceled_by_retention_policy で返ります。本番の報告には入れません。まず reason で3つの表に仕分けます。

仕分け条件次の処理
顧客が解約した条件cancellation_requested かつ feedback あり次の処理手順3の対象
支払い起因条件payment_failed / payment_disputed次の処理件数だけ数え、別の打ち手へ
除外条件canceled_by_retention_policy次の処理報告に含めない

手順3の対象のうち、comment が空でない行だけをClaudeに読ませます。送るのは id、feedback、comment の3列です。顧客IDやメールは載せず、サブスクのIDだけを手がかりに残します。コメントの中にメールアドレスや電話番号、社名が書かれていないかは、正規表現などでローカルで確かめて伏せてから渡します。個人名や連絡先を含む自由記述を外部へ送ってよいかは、自社のプライバシーポリシーと契約に照らして先に決めておく部分です。

落とし穴

コメントに書かれた個人情報をそのまま渡してしまう

自由記述は顧客が自由に書く欄なので、「担当の〇〇さんの対応が」「xxx@example.comに返信してください」のような記述が混ざります。Claudeに渡す前に、ローカルで置換表を使って伏せます。置換表はClaudeに渡しません。手順の型は退職面談の離職理由を分析するClaude手順と同じです。

手順3: コードブックを固定して分類させる

自由記述を毎月ゼロから分類させると、月ごとに分類名が揺れて推移が比べられません。分類の定義、つまりコードブックを先に固定し、それをプロンプトの冒頭に置きます。最初の月は、other の自由記述を数十件読ませて分類案を出させ、人が整えて固定します。

コードブックの一例を示します。Stripeの8つの理由コードを土台に、other から出てきた観点を足す形です。

分類定義理由コードとの関係
価格定義金額、課金方式、割引への不満理由コードとの関係too_expensive と重なる
機能不足定義欲しい機能がない理由コードとの関係missing_features と重なる
乗り換え定義他社や自作へ移る理由コードとの関係switched_service と重なる
利用定着定義使い始められなかった、使わなくなった理由コードとの関係unused と too_complex にまたがる
品質・不具合定義動作の不安定、データの誤り理由コードとの関係low_quality と重なる
サポート定義問い合わせの対応への不満理由コードとの関係customer_service と重なる
事業都合定義顧客側の事業の終了、予算の削減理由コードとの関係該当コードなし
判定不能定義一語や空に近く、読み取れない理由コードとの関係—

右端の列に「該当コードなし」と書いた行が、理由コードだけを追っていては見えない部分です。ここは打ち手が自社の外にあり(顧客の事業終了は防げない)、他の分類と混ぜると改善の効果が薄く見えてしまいます。

指示文の骨格は次のような形にします。

次の表は、解約したStripe顧客の自由記述です(列: id, feedback, comment)。
下のコードブックの分類を1つだけ選び、根拠になった語句を引用してください。
- どれにも当てはまらない場合は「判定不能」とし、理由コードに引きずられず、
  comment の文面だけで判断してください
- 1件に複数の不満がある場合は、主な1つを main に、残りを sub に入れてください
- 出力は CSV(id, main, sub, 根拠の引用, feedbackとの一致: 一致/不一致)
[コードブック]

「feedbackとの一致」列を作らせる点が、このやり方の核です。理由コードと自由記述の分類を突き合わせると、other の中から本来は価格や機能不足だった行が浮かびます。不一致の行の割合そのものが、「選択肢の作りが顧客の言葉に合っていない」ことを示す指標になります。

分類の結果は人が抜き取って確かめる

出力は、そのまま報告に使いません。主な検証は2つです。

  • 件数の多い分類と「判定不能」から、それぞれ数件を抜き取り、根拠の引用が本文に実在するかを見ます
  • 1つの分類が全体の半分を超えたら、コードブックの定義が広すぎないかを疑います

大量の自由記述を安価に回したいときは、自由記述の分類をClaude APIのバッチで回す手順が参考になります。月に数十件程度なら、同期で一度に渡して読み切るほうが簡単です。

手順4: 打ち手とセットで月次報告にまとめる

分類の件数表ができたら、報告の構成を決めます。表だけを出すと「価格が最多」で終わり、会議で次の行動が決まりません。分類ごとに、打ち手の候補と担当を一行で並べます。

分類件数前月比打ち手の例宛先
利用定着件数14前月比増打ち手の例契約後14日の利用状況に応じた案内メール宛先カスタマーサクセス
価格件数9前月比横ばい打ち手の例年額プランの訴求、利用量の少ない層向けの下位プラン宛先プライシング
機能不足件数7前月比減打ち手の例要望の多い機能をロードマップへ宛先プロダクト
支払い起因件数11前月比増打ち手の例カード更新の事前案内、再請求の回数の見直し宛先請求運用

この表の数字は、報告の体裁を示すための仮の値です。実際の件数は、手順3の出力を集計して置き換えます。打ち手の欄は、Claudeに「分類ごとに考えられる施策を2つまで、宛先の部署つきで挙げて」と頼むと下書きが出ます。ただし、自社の事情を知らないまま出る案なので、採用するかどうかは担当者が決めます。

報告には、次の3点も入れます。

  • 層の内訳: 顧客が解約した件、支払い起因の件、除外した件の数
  • 一致率: 理由コードと自由記述の分類が一致した割合と、不一致の代表例を1〜2件
  • 判定不能の件数: 空や一語の記述の件数。これが増えたら、ポータルの設問側を直す話になる

月次の定例にするなら、コードブック、指示文、集計のコマンドを1つのフォルダにまとめ、毎月同じ手順で回します。Claude Codeに「canceled.json を読んで、コードブックどおりに分類し、前月の表と比べて報告の下書きを書いて」と頼む形にすると、再現しやすくなります。全体の月次報告の作り方は月次報告を試算表ExcelからClaudeで作る手順の構成とつなげられます。

分類をStripe側に書き戻すか

解約済みのサブスクは、公式のとおり、metadata と cancellation_details だけは更新できます。そのため、Claudeの分類をサブスクの metadata に書き戻して、Sigmaやダッシュボードからも見えるようにする運用も技術的には可能です。

  • metadata に入れられるのは最大50キー、キー名は40文字まで、値は500文字までです
  • cancellation_details 側に書き込むと、顧客が選んだ理由コードや自由記述と、社内の分類が同じ欄で混ざります。分類は metadata に、別のキー名(例えば churn_category)で入れるほうが原データを守れます

書き戻しは書き込み権限が要る操作です。Stripeコネクタ経由で行う場合は、読み取りと書き込みが別のツールになっているため、読み取りだけで済む月次の分析では書き込みを許可しない設定にできます。分析の段階では書き戻さず、報告が固まってから、人が確認した範囲だけを書き戻すのが無難です。

運用上のつまずき

つまずき

月次で起きやすい崩れ

  • 分類名が月ごとに変わる

    コードブックを固定せずに毎月頼むと、同じ内容が別の名前になります。分類名の追加は、人が承認した月にだけ行います。

  • otherの割合が上がり続ける

    ポータルの選択肢が顧客の言葉に合っていないサインです。分類で多い観点を、次の選択肢に加えられないか検討します。

  • 担当者の入れた comment が混ざる

    ダッシュボードから担当者が解約した行は、顧客の声ではありません。feedback が空で comment だけある行は、別枠に分けます。

まとめ

cancellation_details の集計では、理由コードを数える前に、reason で顧客の判断による解約と支払い起因の解約を分けます。自由記述は主に other の行に入るため、読み直すと本来の分類とのずれが見えます。そのずれの割合を報告に載せると、ポータルの設問そのものを直す根拠になります。Claudeに任せるのは分類の下書きと打ち手の候補で、コードブックの確定と抜き取りの確認、採用する施策の判断は人の役割です。

この記事を共有:XはてブLinkedIn