Zendeskマクロの棚卸しをClaude Codeで進める — 使用回数と重複から整理候補を出す
ZendeskのマクロをAPIで書き出し、usage_30dの使用回数と本文の類似度で、ほとんど使われない定型文と重複を洗い出す手順です。止める前に確かめる点まで書いています。
Zendeskのマクロは、使われないまま増えていきます。担当者が自分用に作った返信、改定前の文面を残した複製、リンク先が消えた案内。管理画面の一覧を目で追うだけでは、どれを畳んでよいか判断できません。
この記事では、マクロをAPIで書き出し、使用回数と本文の類似度から「整理候補」の一覧を作る手順を扱います。数え上げと比較はClaude Codeに書かせたPythonスクリプトが担当し、Claude Code自身は出てきた候補の統合案を考える役に回します。Zendeskのチケットを読ませて分析する方法はZendeskチケット分析にあります。
棚卸しの材料はAPIの3つの値で揃う
マクロ一覧のAPIは、棚卸しに要る情報をほぼ1回の取得で返します。使うのは次の3つです。
棚卸しに使う3つの値
使用回数
include=usage_30dを付けると、直近30日の使用回数が一緒に返ります。1時間・24時間・7日のusage_1h・usage_24h・usage_7dもあります。本文
返信文は
actions配列のうち、fieldがcomment_valueの要素に入っています。有効・更新日時
activeとupdated_atで、止まっているマクロと長く触られていないマクロを分けられます。
使用回数の取得にはプランの条件があります。usage_1h〜usage_30dのサイドロードは、Support ProfessionalまたはSuite Growth以上のプランでだけ使えます。それ未満のプランでは使用回数が取れないので、この記事の手順は「更新日時と類似度だけの棚卸し」に縮みます。
並べ替えにも使用回数を指定できます。sort_byはalphabetical・created_at・updated_at・usage_1h・usage_24h・usage_7d・usage_30dを受け付け、既定はalphabeticalです。sort_orderの既定は、使用回数での並べ替えならdescになります。少ない順に見たいので、ascを明示します。
手順1: 全マクロを書き出す
認証にはOAuthアクセストークンを使います。APIトークンは認証方式の一覧で「deprecated」と表記され、OAuthへの移行を求める注記も付いています。新しく作るスクリプトでAPIトークンに頼る理由はありません。トークンは環境変数に置き、スクリプトに書き込みません。
ページ送りはカーソル方式にします。page[size]を付けるとカーソル方式になり、ほとんどのエンドポイントで上限は100件です。マクロ一覧も1ページ最大100件です。
次のスクリプトは、Claude Codeに「使用回数の少ない順にマクロを全件書き出して」と頼んだときに目指す形の一例です。meta.has_moreとlinks.nextは、ページ送りの解説にあるカーソル方式のレスポンスに合わせています。
export ZENDESK_SUBDOMAIN=your-subdomain
export ZENDESK_OAUTH_TOKEN=... # OAuthアクセストークン
python3 export_macros.py次のコードをexport_macros.pyとして保存してから、上のコマンドを実行します。
import json, os, sys, time, urllib.request, urllib.error
sub = os.environ["ZENDESK_SUBDOMAIN"]
token = os.environ["ZENDESK_OAUTH_TOKEN"]
url = (f"https://{sub}.zendesk.com/api/v2/macros"
"?include=usage_30d&sort_by=usage_30d&sort_order=asc&page[size]=100")
macros = []
while url:
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
try:
with urllib.request.urlopen(req) as res:
data = json.load(res)
except urllib.error.HTTPError as e:
if e.code == 429:
time.sleep(int(e.headers.get("Retry-After", "30")))
continue
raise
macros += data["macros"]
meta = data.get("meta", {})
url = data["links"]["next"] if meta.get("has_more") else None
json.dump(macros, open("macros.json", "w"), ensure_ascii=False, indent=1)
print(len(macros), "macros")429が返ったときはRetry-Afterヘッダーの秒数だけ待つ作りにしています。レート制限は、Suiteのプランごとに、Support APIとHelp Center APIで1分あたり200〜2,500リクエストです。マクロが1,000件あっても取得は10リクエスト前後で済むので、棚卸しだけなら制限に触れにくい量です。
管理者がこのAPIを呼ぶと、アカウント全体のマクロが返ります。担当者個人のマクロも含まれます。個人用を除きたいときはaccess=accountなどで絞れます。access=agentsは、担当者の個人マクロだけを返す管理者向けの指定です。
手順2: 数える部分はスクリプトに任せる
1,000件のマクロ本文をそのままClaude Codeに読ませると、文脈を食い、数え間違いも起きます。重複の判定のように「全組み合わせを比べる」処理は、決まった計算に書き出すほうが確実です。同じ考え方はGTMコンテナの棚卸しでも使っています。
次のスクリプトは、書き出したJSONから整理候補のCSVを作ります。判定は3つです。
- 使用回数が閾値以下のマクロに
low_usageを付ける(引数で変えられ、既定は3回) - 本文を3文字ずつの断片に分け、重なりが7割以上のペアを
similar_idsに並べる - 本文中のURLを
urls列に集める
次のコードをaudit_macros.pyとして保存し、閾値を引数に渡して実行します。
python3 audit_macros.py 3import csv, json, re, sys
THRESHOLD = int(sys.argv[1]) if len(sys.argv) > 1 else 3
macros = json.load(open("macros.json"))
def body(m):
texts = []
for a in m.get("actions", []):
if a.get("field") == "comment_value":
v = a["value"]
texts.append(v if isinstance(v, str) else " ".join(map(str, v)))
return "\n".join(texts)
def shingles(s, n=3):
s = re.sub(r"\s+", "", s)
return {s[i:i + n] for i in range(max(len(s) - n + 1, 1))}
rows = []
for m in macros:
b = body(m)
rows.append({
"id": m["id"], "title": m["title"], "active": m["active"],
"usage_30d": m.get("usage_30d"), "updated_at": m["updated_at"],
"urls": sorted(set(re.findall(r"https?://[^\s)\"<>]+", b))),
"sh": shingles(b), "has_body": bool(b),
})
dups = {}
cands = [r for r in rows if r["has_body"]]
for i, a in enumerate(cands):
for b in cands[i + 1:]:
j = len(a["sh"] & b["sh"]) / len(a["sh"] | b["sh"])
if j >= 0.7:
dups.setdefault(a["id"], []).append(b["id"])
dups.setdefault(b["id"], []).append(a["id"])
with open("macro_report.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["id", "title", "active", "usage_30d", "updated_at",
"low_usage", "similar_ids", "urls"])
for r in rows:
u = r["usage_30d"]
w.writerow([r["id"], r["title"], r["active"], u, r["updated_at"],
u is not None and u <= THRESHOLD,
" ".join(map(str, dups.get(r["id"], []))),
" ".join(r["urls"])])
print("wrote macro_report.csv")comment_valueのvalueは文字列とは限らない前提で、リストの場合は連結しています。この点は公式ページに仕様の記載がないため、保険として入れた処理です。
手元でダミーの3件(返金案内が文面違いで2件、ステータス変更だけのマクロが1件)に対して実行したところ、返金案内の2件が互いをsimilar_idsに挙げ、使用回数0のほうにだけlow_usageが付きました。本文を持たないマクロは類似度の比較から外れます。
手順3: 候補をClaude Codeに読ませて統合案を作る
CSVができたら、読ませるのはlow_usageがTrueの行と、similar_idsが空でない行だけです。全件を渡す必要はありません。
macro_report.csvのうちlow_usageがTrueの行と、similar_idsが空でない行だけを
対象にしてください。macros.jsonから該当IDの本文を引き、
似ているマクロのグループごとに、残す1本と止める候補を表にしてください。
残す理由は、使用回数と更新日時で書いてください。
本文を書き換える案は、この段階では出さないでください。「本文は書き換えない」と最初に縛るのがコツです。統合する前に、どれを残すかの判断だけを先に人が確認できます。統合後の本文を整える作業は、そのあとで別の指示にします。返信文の作り方はClaudeで問い合わせ返信を作るで扱っています。
使用回数が少ないだけでは止められない
使用回数0は、必要がない証拠にはなりません。年に一度の締め処理の案内や、障害時にだけ使う案内文は、直近30日では0でも消せません。usage_30dは直近30日の数字なので、季節性のあるマクロはupdated_atと本文の用途から人が判断します。Claude Codeに出させる表にも、「止めてよいか」ではなく「確認が要る理由」を書かせたほうが安全です。
リンク切れは「集める」までにして、確かめる処理は別にする
urls列は、本文中のURLを集めただけです。リンク先が生きているかは、このスクリプトでは確かめていません。確かめるなら、集めたURLの重複を除いてHEADリクエストを送る小さな処理を足します。社内ドメインや認証付きのヘルプセンターは、ログインなしでは正しく判定できません。ステータスが404だったものだけを「要確認」に挙げ、他は触らない設計にすると誤判定の影響が小さくなります。
手順4: 反映は「無効化」から始める
Zendeskには、マクロを無効にする更新と、削除の2通りがあります。更新はPUT /api/v2/macros/update_manyで、本文に{"macros": [{"id": 25, "active": false}]}の形で複数件を渡せます。削除はDELETE /api/v2/macros/{macro_id}と、IDをカンマ区切りで渡すdestroy_manyです。
整理の最初の一手は、削除ではなくactive: falseにします。公式ページには、削除したマクロの復元に関する記載がありません。無効にしたマクロは、そのまま有効に戻せます。1か月ほど様子を見て、問い合わせが来ない・担当者から声が上がらないと確認できてから消す、という順序が現実的です。
もう一つ、更新APIには落とし穴があります。個別のマクロを更新するとき、actionsを更新すると配列全体が置き換わり、含めなかった他のアクションが消えます。actionsは更新したい分だけでなく、全部を入れ直す必要があります。有効・無効の切り替えだけなら、update_manyのactiveを使うほうが安全です。
反映の進め方
- 1
候補の表を人が確認する
残す1本と止める候補を、担当チームのリーダーが目を通します。
- 2
update_manyでactiveをfalseにする
low_usageと重複の「止める側」だけを対象にします。 - 3
1か月ほど様子を見る
問い合わせ対応の担当者から「消えて困る」という声が出ないか確かめます。
- 4
削除する
声が出なかったものだけ、
destroy_manyで削除します。
マクロ棚卸しを四半期ごとに回す
棚卸しは一度で終わりにせず、四半期に一度などの周期で同じスクリプトを回します。しきい値と類似度の基準は、最初の結果を見て調整します。重複が多すぎる、または拾えていないと感じたら、audit_macros.pyのしきい値を変えるようClaude Codeに頼みます。
手順をCLAUDE.mdに残しておくと、毎回の指示が短くなります。次のような断片が目安です。
## Zendeskマクロ棚卸し
- 認証は環境変数ZENDESK_OAUTH_TOKEN。トークンをファイルに書かない
- 取得はexport_macros.py、判定はaudit_macros.pyに任せ、本文を直接全件読まない
- 本文の書き換え・削除は提案までにして、実行は人が確認してからマクロ本文には、社内の運用手順や顧客対応の方針が入ります。担当者の個人マクロには想定外の内容が混じることがあります。Claude Codeに渡す前に、access=agentsで取れる個人マクロを対象に含めるか決めておきます。
Zendeskと他の業務システムをClaudeで繋ぐ方法はClaude Zendesk連携に、問い合わせからバグ票を作る流れはClaudeで問い合わせをバグ票にするにあります。
まとめ
マクロの棚卸しは、APIで使用回数と本文を取り、数える部分をスクリプトに寄せ、統合の判断だけをClaude Codeに任せる構成で進められます。使用回数のサイドロードはSupport ProfessionalまたはSuite Growth以上のプランが必要です。反映はactive: falseから始め、削除は様子を見てからにするのが、取り返しのつく順序です。