「コンタクト承認くらい、自動化できるだろ」
15分で終わると思っていた。n8nにHTTP Requestノードを3つ並べて、IFで分岐して、終わり。そういうイメージだった。
結果、半日かかった。Chatwork APIの仕様、MCPサーバーのバグ、日本語の漢字——3つの全く違う方向から殴られた記録を書く。
やりたかったこと
Chatworkには「コンタクト承認」という仕組みがある。ダイレクトメッセージを送る前に、相手にコンタクト申請を出して、承認してもらう必要がある。LINEの友だち追加に近い。
問題は、事務局の仕事をしていると申請が大量に来ることだ。講座の受講生が一斉にコンタクト申請を送ってくる。10人、20人。一つずつブラウザで「承認」ボタンを押すのは、正直やってられない。
n8nで15分おきにコンタクト申請を自動チェックし、名簿に載っている人は自動承認する——そういうワークフローを作ろうとした。
罠①:204 No Contentでn8nが死ぬ
まず最初に組んだのは単純な構成だ。
Schedule Trigger(15分) → HTTP Request(GET /incoming_requests) → IF(申請あり) → HTTP Request(PUT /incoming_requests/{id})
テストで動かしてみる。申請がある状態では問題なく動く。だが、申請が0件のときにワークフローが死ぬ。
エラーメッセージ:
ERROR: Invalid JSON in response body
原因はChatwork APIの仕様にある。GET /incoming_requestsは、申請が0件のときHTTP 204 No Contentを返す。レスポンスボディは空。
n8nのHTTP Request node(V4.2)は、デフォルトでレスポンスをJSONとしてパースしようとする。空のボディをJSONパースすれば、当然エラーになる。
試して効かなかったもの
-
onError: "continueRegularOutput"— JSON parseエラーはこの設定では捕捉されない -
options.response.neverError: true— HTTPステータスエラーだけを抑制する。JSON parseは別の話
解決策
responseFormatを"text"に変更し、後続にCodeノードを追加する。
// Codeノード:申請データパース
const rawBody = $input.first().json.data;
if (!rawBody || rawBody.trim() === '') {
// 204 No Content(申請0件)
return [{ json: { request_id: null } }];
}
const requests = JSON.parse(rawBody);
return requests.map(r => ({ json: r }));
後続のIFノードでrequest_idがnullかどうかを判定し、nullならスキップ。これで申請0件のときも死なずに回る。
教訓:HTTP 204を返す可能性があるAPIをn8nで呼ぶときは、responseFormatを"text"にしてCodeノードで手動パースする。
罠②:Chatwork MCPサーバーのAPIパスが違う
n8nで組む前に、まずClaude CodeからChatwork MCP(Model Context Protocol)サーバー経由で手動承認を試みた。#5で書いた通り、34のAPIが全部使える便利なやつだ。だが、すべてのエンドポイントが完璧に動くわけではなかった。
> コンタクト申請一覧を確認して、承認して
申請一覧の取得(list_incoming_requests)は問題なく動く。だが承認(accept_incoming_request)を呼ぶと400エラーが返ってくる。
ソースコード(@chatwork/mcp-server v0.0.4のtoolCallbacks.ts)を読んで原因がわかった。APIのパスが違う。
# Chatwork API仕様
PUT /incoming_requests/{request_id}
# MCPの実装(toolCallbacks.ts)
PUT /incoming_requests/{request_id}/accept ← /acceptが余計
HTTPメソッド(PUT)は合っている。だがパスの末尾に /accept が付いている。Chatwork APIにこのパスは存在しない。結果、400エラーが返る。
回避策
MCPを使わず、curlで直接叩く。
curl -X PUT \
-H "X-ChatWorkToken: YOUR_TOKEN" \
"https://api.chatwork.com/v2/incoming_requests/{request_id}"
n8nのHTTP Request nodeでも同じようにPUTを指定すれば問題ない。MCP経由ではなくAPI直叩きが正解。
教訓:MCPサーバーは便利だが、エラーが出たらソースコードとAPIの仕様書を突き合わせる。MCPが正しいとは限らない。
罠③:「髙橋」と「高橋」は別人
ここまでで技術的な問題は片付いた。あとは名簿との照合ロジックを組むだけ——と思っていた。
コンタクト申請の自動承認で一番重要なのは「この人は本当に名簿に載っている人か」の判定だ。知らない人を自動承認してはいけない。
Chatworkの申請データにはアカウント名が含まれる。名簿にも名前がある。一致すれば承認。単純な話だ。
ところが、ある受講生の照合が失敗した。仮に「高橋さん」としよう。
名簿を見ると「髙橋」と書いてある。Chatworkには「高橋」で登録されている。
わかるだろうか。
名簿:髙橋(はしご高)— U+9AD9
CW :高橋(普通の高)— U+9AD8
Unicodeの別文字だ。 人間の目には同じ「高」に見えるが、コンピュータにとっては完全に別の文字。完全一致でも部分一致(先頭2文字)でもヒットしない。
これは日本語の人名処理ではよくあるハマりポイントだ。異体字のバリエーションは多い:
| よく見る字 | 異体字 | Unicode |
|---|---|---|
| 高 | 髙 | U+9AD8 / U+9AD9 |
| 崎 | 﨑 | U+5D0E / U+FA11 |
| 浜 | 濵 | U+6D5C / U+6FF5 |
| 辺 | 邊 邉 | U+8FBA / U+908A / U+9089 |
| 斎 | 齋 齊 | U+658E / U+9F4B / U+9F4A |
対策の選択肢
- 異体字マッピングテーブルを作る — 髙→高、﨑→崎のような変換表を用意して、照合前に正規化する
- Unicode正規化(NFKC)を適用する — ただし髙→高はNFKCでも変換されない場合がある
- フリガナで照合する — カタカナなら異体字問題が発生しない。名簿にフリガナがあればこちらを使う
今回は手動で「この人です」と確認して対処した。自動化の中に手動が挟まるのは不本意だが、知らない人を自動承認するよりマシだ。
今後の対策としては、漢字の完全一致だけに頼らず、フリガナのフォールバックを用意するのが安全だろう。名簿にフリガナがあればカタカナで照合すれば異体字問題は発生しない。そもそも名簿の設計段階でフリガナ欄を必須にしておくと、この手のトラブルを根元から防げる。
教訓:日本語の人名照合では、漢字一致を信用しすぎない。異体字は思った以上に多い。
完成したワークフロー
最終的に動いている構成はこうなった。
Schedule Trigger(15分おき)
↓
HTTP Request: GET /incoming_requests (responseFormat: text)
↓
Code: 空ボディチェック + JSONパース
↓
IF: request_id が null でない
↓
HTTP Request: GET /名簿API(照合用)
↓
Code: 名前照合(漢字 + フリガナフォールバック)
↓
IF: 名簿に存在する
├─ true → HTTP Request: PUT /incoming_requests/{id}(承認)
└─ false → 通知(手動確認が必要な申請として報告)
申請が来たら15分以内に自動承認。名簿にない人は手動確認に回す。204で死なない。異体字にも対応。
「コンタクト承認くらい簡単だろ」と思っていた半日前の自分に伝えたい。APIの仕様書は隅々まで読め。HTTPメソッドは信用するな。日本語の漢字は信用するな。
まとめ
| 罠 | 原因 | 解決策 |
|---|---|---|
| 204 No Content | Chatwork APIが申請0件で空レスポンスを返す | responseFormatをtextにしてCodeノードで手動パース |
| MCPのパス不一致 | accept_incoming_requestのパスに余計な/acceptが付いている |
API直叩き(PUT)を使う |
| 異体字(髙 vs 高) | Unicodeの別文字で照合失敗 | フリガナフォールバックを用意する |
Chatwork APIは基本的によくできている。だが、エッジケースは自分で踏んで覚えるしかない。204 No Contentを返すAPIは他にもあるかもしれない。MCPサーバーも発展途上だ。異体字問題は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エコシステムに足りないもの
- #21 Googleフォームの回答をChatworkに自動投稿するGAS
- #22 Chatworkの会話を毎日AIが要約してくれる仕組みをn8nで作った話
- #23 chatwork-cliを入れたら、シェルからChatworkが操作できて世界が変わった
- #24 ChatworkのWebhookをn8nで受けるなら、HMAC署名検証は必ずやれ
- #25 Chatwork × GAS × Claude Codeで会員制講座の運用を自動化した