通信:WebSocket、HTTPフォールバック、再接続
ウィジェットとサーバーの通信:アドレス、rc.v1、フレーム形式、エラーと切断コード、上限、seqと重複排除による再接続、SSEとロングポーリングのHTTPフォールバック。
このページでは、ブラウザーのウィジェットとチャットサーバーの間の通信を説明します。開発者ツールのNetworkタブでの問題調査、プロキシやファイアウォールのルール、Content Security Policyに必要な情報です。
重要: 通信プロトコルは、当社のウィジェットと当社のサーバー間のインターフェースです。直接実装した独自クライアントは正式対応ではなく、バージョン1内でフィールドを拡張する場合があります。独自の画面にはJavaScript API、サーバー接続にはWebhookとサーバーAPIを使ってください。
アドレス
- WebSocket:
wss://ws.reactive.chat/rc_wss/ - HTTPフォールバック、オフラインフォーム、アップロード: 同じアドレスの
https://, つまりhttps://ws.reactive.chat/rc_wss/h/…,https://ws.reactive.chat/api/offline/<SITE_KEY>とhttps://ws.reactive.chat/rc_wss/upload/… - ウィジェットファイル:
https://cdn.reactive.chat/
WebSocketアドレスの決定方法:
- ローダータグの
data-rc-ws-url、window.ReactiveChatSettings.wsUrl、ReactiveChat('boot', { wsUrl })。指定があれば、その値を使います。 - なければ、
loader.jsの場所から、wss://ws.+ドメインの最後の2つのラベル+/rc_wss/で導きます。cdn.reactive.chatはwss://ws.reactive.chat/rc_wss/になります。
注意点は2つです。
loader.jsを独自ドメインから配信すると、運営側のドメインのアドレスを導きます。data-rc-ws-urlを設定してください(クイックスタート)。.co.ukなど2部分の末尾では導出を誤るため、data-rc-ws-urlを明示してください。
WebSocket経路はクエリパラメーターを評価しません。
接続の確立
- タイミング: ページ読み込み中ではなく、最初の実際の操作(マウス移動、クリック、キー入力、ホイール、タッチ、フォーカス)で接続します。ウィンドウが開いているか開かれる、既存の会話がある、未送信メッセージがある場合は即座に接続します。任意の要素(通常はローダータグ)の
data-rc-connect="load"かwindow.ReactiveChatSettings.connect = 'load'で、従来どおり読み込み時に接続します。滞在時間後のルールは最初の操作から数えます。 - サブプロトコル:
new WebSocket(url, 'rc.v1')で開き、サーバーもrc.v1を返します。バージョンはアドレスではなく各フレームのv: 1です。 - UTF-8のテキストフレームのみで、1フレームが1メッセージです。圧縮はありません(サーバー側で
permessage-deflateを無効化)。 - ハンドシェイク:
open後、遅くとも5秒以内にinitを送り、サーバーがinit_okを返します。8秒以内にopenがなければ失敗と数え、10秒以内にinit_okがなければ再接続します。 - オリジン検証:
Originのホスト名をプロジェクトのドメイン(許可するオリジン)と比較します。スキームとポートは使わず、サブドメインは一致します(www.your-company.comはyour-company.comに一致、notyour-company.comは不一致)。失敗はorigin_denied、続いて1008で切断し、再試行しません。ドメインが未登録なら検証しないため、必ず登録してください。
フレーム形式
両方向のすべてのメッセージは、次の形式です。
{ "v": 1, "t": "msg", "id": "01JAB3C4D5E6F7G8H9JKMNPQRS", "ts": 1792500000000, "d": { } }
- v(整数、必須) – プロトコルのバージョン。現在
1。 - t(文字列、必須) – 種類。
[a-z0-9_]{1,24}。 - id(文字列、必須) – Crockford Base32の大文字26文字のULID。重複排除のキーでもあります。
- ts(整数、必須) – 1970年からのミリ秒。診断専用。
- d(オブジェクト、必須) – ペイロード。
nullにはせず、空なら{}。
不明な種類とフィールドは無視し、古いウィジェットと新しいサーバーの互換性を保ちます。
メッセージの種類
ウィジェットからサーバーへ:
- init – 接続の登録。サイトキー、セッション、訪問者、タブ、会話ID、言語、同意、任意の署名付き本人情報(
user)、ページ情報(ctx)、再開位置(resume)。 - msg – 訪問者のメッセージ。テキスト、最大5添付、クイック返信やフォームへの回答。
- typing – 入力中表示(
start/stop)。入力欄の内容は決して送信しません。 - read –
seqまで既読。 - ctx – SPAの移動や
identify後などの状況情報変更。 - csat – 評価と最大1,000文字のコメント。
- upload_req – アップロード承認の要求。名前、MIME、サイズ、SHA-256。
- transcript、forget、consent – 会話のメール送信、自分のデータの削除、同意・撤回。
- ping – キープアライブ。
サーバーからウィジェットへ:
- init_ok – 確認したID、履歴、デザイン、機能、上限(下記)、本人検証の結果(
identity.verified)。 - msg – メッセージ。
client_id付きの自分のメッセージのエコーも含む。 - bot_typing、bot_stream – AIが思考中・作成中。
bot_streamは常にそれまでの全文を、最大150 msに1回送信します。 - typing、read、handoff、queue_pos、proactive、conv_list、error、pong。
エラーコード
t: "error"フレームで、d.code、d.message(開発者専用の英語)、d.retry(no、immediate、backoff、after_reinit)、必要に応じd.retry_after_msとd.detailを返します。一覧は固定で、不明なコードはinternalとして扱います。
- bad_json – 無効なJSON。3回で1008切断。
- bad_envelope – 形式違反。ULIDの24時間超のずれも含む(
detail.reason: stale_or_future)。 - unsupported_version – バージョンの合意不可。4001切断。
- unknown_site_key – 不明なキー、一時停止、アーカイブ。4002切断。
- origin_denied – 登録ドメインにないオリジン。1008切断。
- visitor_blocked – 訪問者をブロック。1008切断。
- auth_failed – 本人確認失敗。
detail.reasonはno_hash、bad_hash、bad_exp、expired、exp_too_far。匿名で続行します。 - not_initialized/already_initialized –
init前のメッセージ、または2回目のinit。 - init_timeout – 5秒以内に
initなし。4004切断。 - conversation_not_found/conversation_closed
- rate_limited – 頻度制限。
retry_after_ms付き。同じ接続で繰り返すと4008切断。 - payload_too_large – 64 KiB超のフレーム。
- msg_too_long – プラン上限超。
detail.max。 - unsupported_kind、attachment_invalid
- upload_disabled、upload_too_large、upload_mime_denied、upload_expired
- consent_required – 操作に同意が必要。
- dnd_active – 「通知しない」が有効。
- session_conflict – 同一セッションの接続が3を超える。4003切断し、タブをフォロワーにする。
- internal、unavailable – 一時的。待機後に再試行。
切断コード
- 1000、1001、1006、1011、1013、4004 – 待機して再接続。
- 1008 – オリジン拒否、無効JSONの繰り返し、不正利用対策のブロック。このページでは再試行しません。
- 4001 – バージョンの合意不可。再試行なし。
- 4002 – 不明なキー、非アクティブ。再試行なし。
- 4008 – 頻度制限の繰り返し。
retry_after_ms待って再接続。 - 4003 – セッション競合。他のタブのフォロワーとして動作。
接続ごとの上限
サーバーがinit_ok.limitsとinit_ok.featuresで、適用値を送ります。
- heartbeat_ms 25,000 – 最後の送信から25秒後に
ping。 - pong_timeout_ms 10,000 –
pongが2回欠けると再接続。 - idle_timeout_ms 70,000 – 70秒受信フレームがないと切断(1001)。
- max_frame_bytes 65,536 – 超過は
payload_too_large。 - msg_per_10s 20、frames_per_10s 120 – 接続ごとのトークンバケット。超過は
rate_limited。 - reconnect_cap_ms 30,000 – 最大再試行間隔。
- dedup_window_ms 86,400,000 – 24時間の重複排除。
- features.max_msg_len – Unicodeコードポイントで、Freeは1,000、有料は4,000。超過テキストは送信しません。
- features.max_upload_bytes、features.allowed_mime – 上限と利用枠参照。
再接続、順序、重複
- 待機時間:
min(30000, 500 × 2^(attempt−1))msの上半分からランダムに選びます。初回250~500 ms、7回目以降15~30秒。バックグラウンドは3倍です。サーバーのretry_after_msを下限とします。回数は無制限で、init_ok時だけリセットします。ブラウザーがonlineを通知すると即時試行し、オフラインなら待ちます。 - 順序: 会話の各メッセージに、1から始まる欠番なしの
seqがあります。時刻ではなくseq順に表示します。再接続時にresume.last_seqと最大50個の保留IDを送り、サーバーが続き(最大100件、それ以上はhas_more)と、受信済みメッセージの確認を再送します。 - 重複なし:
idを24時間記憶し、再送を保存せず同じ確認を返します。ウィジェットの「再送信」も同じidを使います。 - 送信の状態:
pending→sent→delivered(サーバーのエコー)→read。5回の試行後、24時間後、retry: "no"時にfailed。 - オフラインバッファー: 接続断時、最大50件、128 KiB、24時間保持します。同意ありは
localStorage、なしはsessionStorage。次のinit_okで送ります。 - 複数タブで1接続: Web Locks、なければ
localStorageのキープアライブでリーダーを選び、リーダーだけが接続を保持します。他のタブはBroadcastChannelで通信します。
HTTPフォールバック
WebSocketが通らない場合、同じフレームと上限の通常のHTTPSで続行します。ウィジェットのみが使い、オペレーター画面は常にWebSocketです。短い説明はHTTPフォールバックをご覧ください。
適用条件:
- ブラウザーがWebSocketに未対応。
- このページで一度もWebSocketが確立する前に2回連続失敗。8秒で結果がない場合も失敗。
sessionStorageのrc_httpが30分未満。同じタブの後続ページ。- 起動時に存在する任意の要素の
data-rc-transport="http"、またはローダーの前のwindow.ReactiveChatSettings = { transport: 'http' }で強制。ReactiveChat('boot', { transport: 'http' })には効果がなく、起動時に決まります。
fetchがないブラウザーでは使えません。既存のWebSocketが切れた場合はWebSocketで再接続します。切り替え後はページごとに1回、WebSocketが通るか確認し、通ればrc_httpを削除して次のページから戻します。
リクエスト(https://ws.reactive.chat/rc_wss/h/ 配下):
- POST h/auf – 接続を開く。応答
{"v":1,"cid":"…","tok":"…","poll_ms":25000,"idle_ms":60000}。tokは32文字の短期接続トークンです。 - GET h/<cid>/sse?tok=… – Server-Sent Events。フレームごとに
id:とdata:、15秒ごとにキープアライブ。欠落はLast-Event-IDで配信。 - GET h/<cid>/poll?tok=…&nach=<n> – ロングポーリング。最大25秒待ち、
{"v":1,"n":…,"rahmen":[…]}で応答。 - POST h/<cid>/tx?tok=… –
text/plainで、1フレームまたは最大20フレームの配列を送信。応答204。 - POST h/<cid>/zu?tok=… –
sendBeaconで閉じる。
最初にSSEを試し、プロキシのバッファリングなどで5秒以内に最初のフレームが来なければ、ロングポーリングに切り替えます。404(unbekannt)、403、413、429などは切断と同様に扱い、待機後に再試行します。60秒リクエストがなければサーバーが閉じます。CORSのAccess-Control-Allow-Originはオリジンを反映し、認証情報は含めません。オリジン検証はWebSocketと同じくinit時です。
運営側の要件:
- CSPの
connect-srcに、wss://とhttps://. 両方のチャットアドレスを含める。 - ファイアウォールとプロキシで、最低30秒のリクエストを許可する。
- 独自プロキシが
/rc_wss/をバッファリングせずに通す。nginxはproxy_buffering offとproxy_cache off、Apacheはflushpackets=on、Caddyはflush_interval -1。/rc_wss/h/はクエリにトークンを含むためアクセスログから除外します。独自ドメイン参照。
ブラウザーの保存: sessionStorageのrc_http項目にHTTPS接続の時刻だけを保存し、個人データは含みません。
使用中の経路
開発者ツールのNetworkタブで確認します。
- WebSocket:
wss://ws.reactive.chat/rc_wss/(または独自サブドメイン)へのWebSocket接続。フレームにinit、init_ok、ping/pong、メッセージがあります。 - HTTPフォールバック: 最初に
h/auf、次に長時間の…/sse(または繰り返す…/poll)、送信ごとに…/tx。
次:上限と利用枠 · トラブルシューティング
最終更新: