17本目の記事を公開した夜、ふと手が止まった。
Chatwork APIで未読管理をやった。FAQ抽出もやった。人間関係マップも、Webhookも、ファイルAPIも、タスクAPIも。MCP接続、n8n巡回、果てはClaude Code Channelsまで。「Chatwork APIでできること」は、たぶん相当やり尽くした方だと思う。
だからこそ見えてきた。「ここが足りない」が。
これは批判じゃない。17本書くほど使い倒しているユーザーからの、率直な「こうなったら最高なのに」だ。
足りないもの①: スレッド/リプライのAPI
Chatworkにはリプライ機能がある。
[rp aid=12345 to=67890-1234567890]
そのやり方でOKです
GUIからは普通に使える。問題はAPIだ。
GET /rooms/{room_id}/messages でメッセージは取れる。だが「このメッセージへのリプライ一覧」を取得する手段がない。
# こういうエンドポイントが存在しない
curl -X GET "https://api.chatwork.com/v2/rooms/{room_id}/messages/{message_id}/replies" \
-H "X-ChatworkToken: $TOKEN"
#9でFAQ抽出パイプラインを作ったとき、これが地味に痛かった。質問メッセージとその回答リプライを紐付けたかったのに、APIからはリプライチェーンを辿れない。結局、メッセージ本文に含まれる [rp aid=xxx to=xxx] タグを正規表現でパースして、自前で紐付けた。
import re
def extract_reply_target(body: str) -> dict | None:
"""メッセージ本文からリプライ先を抽出する"""
match = re.search(r'\[rp aid=(\d+) to=(\d+)-(\d+)\]', body)
if match:
return {
"account_id": int(match.group(1)),
"room_id": int(match.group(2)),
"message_id": match.group(3)
}
return None
動く。動くが、公式サポートされた方法じゃない。Chatworkが [rp] タグの仕様を変えたら壊れる。
Slackには conversations.replies がある。thread_tsを指定すれば、そのスレッドの全リプライが返ってくる。これだけで会話の文脈を機械的に追える。
# Slackならこれでスレッドが取れる
curl -X POST "https://slack.com/api/conversations.replies" \
-H "Authorization: Bearer $SLACK_TOKEN" \
-d "channel=C1234567890" \
-d "ts=1234567890.123456"
Chatworkにも、これに相当するものが欲しい。リプライの構造化はチャットツールのAPIとして基本機能だと思っている。
足りないもの②: リアクション(絵文字リアクション)
2023年ごろ、Chatworkにリアクション機能が追加された。メッセージに👍や❤️を付けられるようになった。
GUIでは普通に使えるのだが、APIにリアクション関連のエンドポイントが一切ない。
# こういうのが欲しい
# リアクション取得
GET /rooms/{room_id}/messages/{message_id}/reactions
# リアクション追加
POST /rooms/{room_id}/messages/{message_id}/reactions
{"emoji": "thumbsup"}
リアクションは「読んだ」のシグナルだ。既読(read)よりも軽い「了解」「OK」を表現できる。メールの開封通知に近い、だが押しつけがましくない。
自動化の観点で言うと、リアクション集計ができたらかなり面白い。
- 重要な告知メッセージに、何人がリアクションしたか
- チーム内の「暗黙の合意」をリアクション数で可視化する
- リアクションゼロのメッセージ=誰も見ていない可能性がある
「見た」をAPIで取れないのは、地味にもったいない。
足りないもの③: メッセージ検索API
Chatworkの画面には検索窓がある。キーワードを入れれば過去メッセージを探せる。
だが、APIに検索エンドポイントがない。
# こういうのが存在しない
GET /rooms/{room_id}/messages/search?q=keyword&from=2026-01-01
#11で詳しく書いたが、GET /rooms/{room_id}/messages は最新100件しか返さない。過去メッセージを全件取得するには force=1 パラメータを付けて何度もポーリングする必要がある。
# 100件ずつ遡る苦行
curl -s "https://api.chatwork.com/v2/rooms/${ROOM_ID}/messages?force=1" \
-H "X-ChatworkToken: $TOKEN"
# → 最大100件。もっと古いのが欲しければ、最古のmessage_idを控えて再リクエスト
# → これを繰り返す。数千件あると数十回のAPIコール
FAQ抽出(#9)では、8ルーム・数千件のメッセージをこの方法で全件取得した。動くには動く。だがレート制限(300回/5分)との戦いになる。
Slackの search.messages はこう書ける。
curl -X GET "https://slack.com/api/search.messages?query=keyword&sort=timestamp" \
-H "Authorization: Bearer $SLACK_TOKEN"
一発で目的のメッセージに辿り着ける。全件取得してからgrepするのと、検索APIを叩くのでは、コスト感覚がまるで違う。
正直、これが一番「実装されたら世界が変わる」機能だと思っている。
足りないもの④: Webhookの種類
#10でWebhook署名検証の記事を書いた。Chatwork Webhookは動く。動くのだが、種類が足りない。
現状のWebhookイベントはこのあたり。
- メッセージ作成
- メンバー変更(追加・削除)
足りないのはこの3つ。
タスク期限到来Webhook。 タスクの期限が来たことをWebhookで通知してほしい。現状は自前でポーリングしてタスク一覧を取得し、期限を比較するしかない。
# 現状: 自前でタスク期限チェック
import datetime
tasks = get_room_tasks(room_id)
now = datetime.datetime.now()
for task in tasks:
if task["limit_time"] and task["limit_time"] < now.timestamp():
if task["status"] == "open":
notify(f"期限切れタスク: {task['body']}")
ファイルアップロードWebhook。 ファイルが上がったら自動でバックアップする、という処理を組みたい。現状はメッセージ作成Webhookを受け取って、メッセージ本文に [dw] タグ(ダウンロードリンク)が含まれているかで判定するしかない。
メンション通知Webhook。 「自分宛のメンションだけを拾いたい」ニーズは大きいはずだ。全メッセージを受け取ってから [To:自分のaccount_id] をフィルタリングする手間が消える。
// 現状: 全メッセージを受けてからフィルタ
app.post('/webhook', (req, res) => {
const body = req.body.webhook_event.body;
const myAccountId = 'XXXXXXX';
if (body.includes(`[To:${myAccountId}]`)) {
// 自分宛の処理
handleMention(req.body);
}
// それ以外は捨てる ← この「捨てる」が無駄
res.sendStatus(200);
});
Webhook1種類につきAPIコール1回分のフィルタリングコストが消える。チリも積もれば山になる話だ。
足りないもの⑤: WebSocket/リアルタイム接続
現状、Chatwork APIにリアルタイム接続の手段がない。
#17でClaude Code Channelsを実装したとき、メッセージ検知は10秒間隔のポーリングだった。
// 10秒ごとにメッセージを取りに行く
setInterval(async () => {
const messages = await fetchNewMessages(roomId);
if (messages.length > 0) {
await processMessages(messages);
}
}, 10000);
10秒。人間の会話ならギリギリ許容範囲だ。だが「リアルタイム」とは呼べない。
ポーリング間隔を短くすると、レート制限に引っかかる。300回/5分=1回/秒が上限。複数ルームを監視すると、すぐに枠を食い尽くす。間隔を長くすると、反応が遅くなる。
このジレンマをWebSocketは根本解決する。
// WebSocketがあったらこうなる(妄想)
const ws = new WebSocket('wss://api.chatwork.com/v2/stream');
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'subscribe',
rooms: [roomId],
events: ['message_created', 'task_updated']
}));
});
ws.on('message', (data) => {
const event = JSON.parse(data);
// 即座にイベントが届く。ポーリング不要
handleEvent(event);
});
Slackの socket_mode がまさにこれだ。接続を張りっぱなしにして、イベントが来たら即座に処理する。サーバーの負荷もクライアントの負荷も、ポーリングより遥かに小さい。
Claude Code Channelsを「リアルタイムチャット」と呼ぶには、正直10秒のラグが引っかかっていた。WebSocketがあれば、このラグが消える。
足りないもの⑥: リッチテキスト/マークダウン対応
Chatworkのメッセージフォーマットは独自記法だ。
[info][title]お知らせ[/title]
本日のミーティングは15時からです。
[hr]
詳細は[b]添付ファイル[/b]を参照してください。
[code]
const greeting = "hello";
[/code]
[/info]
マークダウンじゃない。**太字** は効かない。[b]太字[/b] と書く必要がある。
APIからメッセージを投稿するとき、毎回Chatwork記法に変換するラッパーを書くことになる。
def markdown_to_chatwork(md: str) -> str:
"""MarkdownをChatwork記法に変換する"""
text = md
# 太字
text = re.sub(r'\*\*(.*?)\*\*', r'[b]\1[/b]', text)
# コードブロック
text = re.sub(r'```(\w*)\n(.*?)```', r'[code]\2[/code]', text, flags=re.DOTALL)
# 水平線
text = text.replace('---', '[hr]')
# インフォボックス(Chatwork独自)
# → マークダウンには対応するものがないので、独自ルールで変換
return text
MCP経由でClaude Codeからメッセージを送るとき、Claude Codeはマークダウンで考える。それをChatwork記法に変換してから投稿する。この変換レイヤーが、地味に面倒だ。
マークダウンをネイティブサポートしてくれたら、この変換が丸ごと消える。もしくは、APIリクエストに format: "markdown" を指定したら、サーバー側で変換してくれるだけでもいい。
足りているもの(フェアに書く)
ここまで「足りない」を並べた。フェアじゃないので、足りているものも書く。
基本のCRUDは揃っている。 メッセージ・ルーム・メンバー・タスク・ファイル。必要な操作は一通りできる。17記事書けたのは、この基盤があったからだ。
認証がシンプル。 APIトークンをヘッダに載せるだけ。
curl -H "X-ChatworkToken: $TOKEN" \
"https://api.chatwork.com/v2/me"
OAuth2の認証フローを組む必要がない。個人利用・チーム利用なら、これで十分だ。セットアップに5分かからない。
レート制限が明確。 300回/5分。レスポンスヘッダに残り回数が入っている。
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1711234567
暗黙のレート制限で急に429が返ってくるAPIとは違う。設計しやすい。
JSONが素直。 ネストが浅く、キー名が直感的。パースで悩むことがほぼない。
{
"message_id": "12345",
"account": {
"account_id": 67890,
"name": "田中太郎"
},
"body": "メッセージ本文",
"send_time": 1711234567,
"update_time": 0
}
send_time がUnixタイムスタンプなのは好みが分かれるが、一貫していて扱いやすい。
ドキュメントが日本語で充実。 これは日本企業の強みだ。英語のドキュメントを読み解く必要がない。APIリファレンスに日本語のサンプルコードが付いている。
「足りない」から自分で作った17本
振り返ると、シリーズの半分以上は「足りないものを自分で作った」記事だ。
#17のClaude Code Channels。WebSocketもリアルタイム接続もなかったから、ポーリングで自作した。
#9のFAQ抽出パイプライン。検索APIがなかったから、全件取得してNLPで分類した。
#14のn8n全ルーム巡回。全ルーム横断の未読管理がなかったから、n8nで組んだ。
#12の既読制御。既読管理が思い通りにいかなかったから、read と unread を組み合わせて自前で制御した。
APIの「足りない」は「自分の出番」だ。足りていたら記事は17本も書けなかった。
足りないから調べる。調べるから深く理解する。理解するから記事が書ける。記事を書くからまた新しい「足りない」が見つかる。
この循環が回っている限り、シリーズは続く。
最後に
17本書いて、一番感じたのは「Chatwork APIは過不足ない基本機能がある」ということだ。過不足ないからこそ、その上に自分で積める。
足りないものを数えるのは簡単だ。でも、足りないところを自分で埋めてきた17記事分の経験の方が、よほど価値がある。
ただ——スレッドAPIだけは本当に欲しい。
Chatworkさん、お願いします。
Chatworkシリーズ
- #1 なぜ2026年にまだChatworkを使い倒しているのか
- #2 chatwork-client-gas、ぶっちゃけいるの?
- #3 ルームの参加者データだけで、組織の人間関係マップを作った
- #4 「Chatworkに確定連絡が来たら請求書を送る」をGASで自動化する
- #5 Chatwork MCPを繋いだら、17ルームの未読が10秒で片付いた
- #6 MCP vs GAS — Chatwork自動化の「正解」はどっちか
- #7 コンタクト承認をn8nで自動化しようとしたら、3つの罠にハマった
- #8 ChatworkにAIチームを住まわせたら、勝手に会話が始まった
- #9 Chatwork 8ルームの全メッセージからFAQ429件を自動抽出した
- #10 Webhook署名検証を入れたら全メッセージが消えた
- #11 過去メッセージを全件取得しようとしたら、APIの「100件の壁」にハマった
- #12 Chatwork APIの「既読」は自分で制御できる
- #13 Chatwork APIのファイル機能、使ったことある?
- #14 n8nで全ルーム巡回
- #15 タスク機能をAPIで使い倒す
- #16 MCPを2アカウント同時接続したら、仕事用と事務局用が1画面で回った
- #17 【世界初かもしれない】ChatworkでClaude Code Channelsを実装してみた
- #18 Chatwork × Dify × GASで問い合わせ回答を自動提案する
- #19 RelationMapを夜間バッチで毎日自動更新する
- #20 17記事書いて見えた、Chatwork APIエコシステムに足りないもの(この記事)
Chatworkシリーズ
- #1 なぜ2026年にまだChatworkを使い倒しているのか
- #2 chatwork-client-gas、ぶっちゃけいるの?
- #3 ルームの参加者データだけで、組織の人間関係マップを作った
- #4 「Chatworkに確定連絡が来たら請求書を送る」をGASで自動化する
- #5 Chatwork MCPを繋いだら、17ルームの未読が10秒で片付いた
- #6 MCP vs GAS — Chatwork自動化の「正解」はどっちか
- #7 コンタクト承認をn8nで自動化しようとしたら、3つの罠にハマった
- #8 ChatworkにAIチームを住まわせたら、勝手に会話が始まった
- #9 Chatwork 8ルームの全メッセージからFAQ429件を自動抽出した
- #10 Webhook署名検証を入れたら全メッセージが消えた
- #11 過去メッセージを全件取得しようとしたら、APIの「100件の壁」にハマった
- #12 Chatwork APIの「既読」は自分で制御できる
- #13 Chatwork APIのファイル機能、使ったことある?
- #14 n8nで全ルーム巡回
- #15 タスク機能をAPIで使い倒す
- #16 MCPを2アカウント同時接続したら、仕事用と事務局用が1画面で回った
- #17 【世界初かもしれない】ChatworkでClaude Code Channelsを実装してみた
- #18 Chatwork × Dify × GASで問い合わせ回答を自動提案する
- #19 RelationMapを夜間バッチで毎日自動更新する
- #20 17記事書いて見えた、Chatwork APIエコシステムに足りないもの(この記事)
- #21 Googleフォームの回答をChatworkに自動投稿するGAS
- #22 Chatworkの会話を毎日AIが要約してくれる仕組みをn8nで作った話
- #23 chatwork-cliを入れたら、シェルからChatworkが操作できて世界が変わった
- #24 ChatworkのWebhookをn8nで受けるなら、HMAC署名検証は必ずやれ
- #25 Chatwork × GAS × Claude Codeで会員制講座の運用を自動化した