Transport: WebSocket, HTTP-fallback en opnieuw verbinden
Hoe de widget met de chatserver communiceert: adressen, subprotocol rc.v1, frameformaat, fout- en sluitcodes, limieten, opnieuw verbinden met seq en deduplicatie en HTTP-fallback via Server-Sent Events en long polling.
Deze pagina beschrijft wat er tussen de widget in de browser en de chatserver gebeurt. U hebt deze informatie nodig voor probleemoplossing (netwerktabblad van de ontwikkelaarstools), voor proxy- en firewallregels en voor uw Content Security Policy.
Belangrijk: het communicatieprotocol is de interface tussen onze widget en onze server. Eigen clients die rechtstreeks het protocol spreken, worden niet officieel ondersteund; velden kunnen binnen protocolversie 1 worden uitgebreid. Gebruik voor uw eigen interfaces de JavaScript-API, voor serverkoppelingen webhooks en de server-API.
Adressen
- WebSocket:
wss://ws.reactive.chat/rc_wss/ - HTTP-fallback, offlineformulier, uploads: hetzelfde adres met
https://, dushttps://ws.reactive.chat/rc_wss/h/…,https://ws.reactive.chat/api/offline/<SITE_KEY>enhttps://ws.reactive.chat/rc_wss/upload/… - Widgetbestanden:
https://cdn.reactive.chat/
Hoe de widget het WebSocket-adres vindt:
data-rc-ws-urlop de loadertag,window.ReactiveChatSettings.wsUrlofReactiveChat('boot', { wsUrl })– indien ingesteld, geldt deze waarde.- Anders wordt het afgeleid van de locatie van
loader.js:wss://ws.+ de laatste twee delen van de domeinnaam +/rc_wss/.cdn.reactive.chatwordtwss://ws.reactive.chat/rc_wss/.
Hieruit volgen twee valkuilen:
- Als u
loader.jsvanaf uw eigen domein aanbiedt, leidt de widget een adres onder uw domein af. Stel in dat gevaldata-rc-ws-urlin (zie Snel aan de slag). - Bij tweedelige uitgangen zoals
.co.ukgaat de afleiding fout. Steldata-rc-ws-urlexpliciet in.
De WebSocket-route verwerkt geen queryparameters.
De verbinding opzetten
- Moment: de widget maakt niet tijdens het laden van de pagina verbinding, maar bij de eerste echte interactie (muisbeweging, klik, toetsaanslag, muiswiel, aanraking, focus). Er wordt direct verbinding gemaakt als het chatvenster open is of wordt geopend, een gesprek van de bezoeker bekend is of er niet-verzonden berichten wachten. Met
data-rc-connect="load"op een willekeurig element (meestal de loadertag) ofwindow.ReactiveChatSettings.connect = 'load'wordt zoals voorheen bij het laden verbinding gemaakt. Regels die een bepaalde tijd na de paginaweergave actief worden, tellen vanaf de eerste interactie. - Subprotocol: de widget opent
new WebSocket(url, 'rc.v1')en de server antwoordt metrc.v1. De protocolversie staat niet in het adres, maar in elk frame (v: 1). - Alleen tekstframes, in UTF-8; één frame = één bericht. Geen compressie (
permessage-deflatestaat aan de serverzijde uit). - Handshake: na
openverstuurt de widget uiterlijk binnen 5 secondeninit; de server antwoordt metinit_ok. Alsopenniet binnen 8 seconden komt, geldt de poging als mislukt; alsinit_okniet binnen 10 seconden komt, maakt de widget opnieuw verbinding. - Origincontrole: de server vergelijkt de hostnaam uit de
Origin-header met de domeinen van het project (Toegestane origins in uw account). Schema en poort tellen niet mee; subdomeinen komen overeen (www.your-company.compast bijyour-company.com,notyour-company.comniet). Bij mislukking: foutorigin_denied, daarna sluiten met 1008, geen nieuwe poging. Als er geen domein in het project is ingevuld, vindt er geen origincontrole plaats — vul daarom altijd uw domeinen in.
Frameformaat
Elk bericht heeft in beide richtingen deze vorm:
{ "v": 1, "t": "msg", "id": "01JAB3C4D5E6F7G8H9JKMNPQRS", "ts": 1792500000000, "d": { } }
- v (geheel getal, verplicht) – protocolversie, momenteel
1. - t (tekenreeks, verplicht) – berichttype,
[a-z0-9_]{1,24}. - id (tekenreeks, verplicht) – ULID: 26 tekens Crockford Base32, alleen hoofdletters. Dit is ook de sleutel voor deduplicatie.
- ts (geheel getal, verplicht) – milliseconden sinds 1970, alleen voor diagnose.
- d (object, verplicht) – gegevens, nooit
null,{}als het leeg is.
Onbekende typen en velden worden zonder melding genegeerd — zo blijven oudere widgets compatibel met nieuwere servers.
Berichttypen in één oogopslag
Van de widget naar de server:
- init – aanmelding van de verbinding: sitesleutel, sessie-, bezoeker-, tabblad- en gespreks-ID, taal, toestemmingsstatus, de eventuele ondertekende identiteit (
user), paginacontext (ctx) en hervattingspunt (resume). - msg – een bezoekersbericht (tekst, maximaal 5 bijlagen, antwoord op snelantwoorden of formulieren).
- typing – type-indicator (
start/stop). De inhoud van het invoerveld wordt nooit verstuurd. - read – gelezen tot en met
seq. - ctx – contextwijziging, bijvoorbeeld bij navigatie in een SPA of na
identify. - csat – beoordeling (duim omhoog/omlaag, opmerking van maximaal 1000 tekens).
- upload_req – verzoek om toestemming voor upload (naam, MIME-type, grootte, SHA-256).
- transcript, forget, consent – gesprek per e-mail, eigen gegevens verwijderen, toestemming geven/intrekken.
- ping – keep-alive.
Van de server naar de widget:
- init_ok – bevestigde ID's, geschiedenis, ontwerp, functies en limieten (zie hieronder), resultaat van de identiteitscontrole (
identity.verified). - msg – berichten (inclusief de echo van het eigen bericht van de bezoeker met
client_id). - bot_typing, bot_stream – de AI-assistent denkt na of schrijft;
bot_streamverstuurt altijd de volledige tot dan toe zichtbare tekst, maximaal elke 150 ms. - typing, read, handoff, queue_pos, proactive, conv_list, error, pong.
Foutcodes
Een fout komt binnen als een frame t: "error" met d.code, d.message (Engels, alleen voor ontwikkelaars), d.retry (no, immediate, backoff, after_reinit) en waar nodig d.retry_after_ms en d.detail. De lijst is gesloten; een onbekende code wordt als internal behandeld.
- bad_json – geen geldige JSON; na drie keer sluiten met 1008.
- bad_envelope – het frame voldoet niet aan het formaat (ook: ULID wijkt meer dan 24 uur af,
detail.reason: stale_or_future). - unsupported_version – versie kan niet worden overeengekomen; sluiten met 4001.
- unknown_site_key – sitesleutel onbekend, project gepauzeerd of gearchiveerd; sluiten met 4002.
- origin_denied – origin hoort niet bij de domeinen van het project; sluiten met 1008.
- visitor_blocked – bezoeker geblokkeerd; sluiten met 1008.
- auth_failed – identiteitscontrole mislukt,
detail.reason:no_hash,bad_hash,bad_exp,expired,exp_too_far. De sessie gaat anoniem verder. - not_initialized / already_initialized – bericht vóór
init, of een tweedeinit. - init_timeout – geen
initbinnen 5 s; sluiten met 4004. - conversation_not_found / conversation_closed
- rate_limited – verzoeklimiet; met
retry_after_ms. Als dit herhaaldelijk op dezelfde verbinding gebeurt, sluit de server met 4008. - payload_too_large – frame groter dan 64 KiB.
- msg_too_long – tekst langer dan de abonnementsgrens,
detail.max. - unsupported_kind, attachment_invalid
- upload_disabled, upload_too_large, upload_mime_denied, upload_expired
- consent_required – voor de actie is toestemming nodig.
- dnd_active – ‘Niet storen’ is actief.
- session_conflict – te veel verbindingen van dezelfde sessie (meer dan 3); sluiten met 4003, het tabblad wordt een volger.
- internal, unavailable – tijdelijk; na een wachttijd opnieuw proberen.
Sluitcodes
- 1000, 1001, 1006, 1011, 1013, 4004 – de widget maakt opnieuw verbinding (na een wachttijd).
- 1008 – origin geweigerd, herhaaldelijk ongeldige JSON, blokkering door bescherming tegen misbruik. Geen nieuwe poging op deze pagina.
- 4001 – versie kan niet worden overeengekomen. Geen nieuwe poging.
- 4002 – sitesleutel onbekend of project niet actief. Geen nieuwe poging.
- 4008 – herhaalde overschrijding van de verzoeklimiet. De widget wacht
retry_after_msen maakt opnieuw verbinding. - 4003 – sessieconflict; het tabblad werkt als volger van een ander tabblad.
Limieten per verbinding
De server stuurt de geldende waarden in init_ok.limits en init_ok.features:
- heartbeat_ms 25 000 – de widget verstuurt 25 s na het laatste uitgaande bericht een
ping. - pong_timeout_ms 10 000 – twee ontbrekende
pongs leiden tot opnieuw verbinden. - idle_timeout_ms 70 000 – de server sluit na 70 s zonder inkomend frame (1001).
- max_frame_bytes 65 536 – grotere frames:
payload_too_large. - msg_per_10s 20 en frames_per_10s 120 – token bucket per verbinding; daarboven
rate_limited. - reconnect_cap_ms 30 000 – langste wachttijd tussen nieuwe pogingen.
- dedup_window_ms 86 400 000 – 24 uur deduplicatie.
- features.max_msg_len – tekens (Unicode-codepunten) per bezoekersbericht: 1000 in het Free-abonnement, 4000 in betaalde abonnementen. De widget verstuurt geen te lange teksten.
- features.max_upload_bytes, features.allowed_mime – zie Limieten en quota.
Opnieuw verbinden, volgorde, duplicaten
- Wachttijd:
min(30000, 500 × 2^(attempt−1))ms, waarvan een willekeurige waarde uit de bovenste helft wordt gebruikt (poging 1: 250–500 ms, vanaf poging 7: 15–30 s). Driemaal zo lang in achtergrondtabbladen. Eenretry_after_msvan de server is de ondergrens. Het aantal pogingen is onbeperkt; de teller wordt pas bijinit_okteruggezet. Als de browseronlinemeldt, probeert de widget het direct; als de browser offline is, wordt gewacht. - Volgorde: elk bericht van een gesprek heeft een aaneengesloten
seqvanaf 1. Berichten worden opseqweergegeven, niet op tijd. Bij opnieuw verbinden verstuurt de widgetresume.last_seqen maximaal 50 wachtende ID's; de server levert alles daarna (maximaal 100 berichten, andershas_more) en herhaalt de bevestigingen voor al ontvangen berichten. - Geen duplicaten: de server onthoudt elke bericht-
id24 uur. Een herhaald bericht wordt niet opnieuw opgeslagen, maar krijgt dezelfde bevestiging. ‘Opnieuw verzenden’ in de widget gebruikt dezelfdeid. - Statussen van een uitgaand bericht:
pending→sent→delivered(echo van de server) →read;failedna 5 pogingen, na 24 uur of bijretry: "no". - Offlinebuffer: als de verbinding wegvalt, komen berichten in een buffer (maximaal 50 vermeldingen, 128 KiB, 24 uur) — met toestemming in
localStorage, zonder toestemming insessionStorage— en worden bij de volgendeinit_okverzonden. - Meerdere tabbladen, één verbinding: de tabbladen van een website kiezen een leider (Web Locks, anders een keep-alive in
localStorage); alleen de leider houdt de verbinding open en de overige tabbladen communiceren viaBroadcastChannel.
HTTP-fallback
Als een netwerk WebSocket blokkeert, gaat de chat verder via gewone HTTPS-verzoeken, met dezelfde frames en limieten. Alleen de widget gebruikt dit; de medewerkersinterface maakt altijd via WebSocket verbinding. De korte uitleg voor redacteuren staat onder HTTP-fallback.
Wanneer dit geldt:
- de browser ondersteunt WebSocket niet;
- twee WebSocket-pogingen mislukken achter elkaar voordat er ooit een WebSocket op deze pagina tot stand is gekomen (een poging zonder resultaat na 8 seconden geldt als mislukt);
sessionStoragebevat een vermeldingrc_httpdie minder dan 30 minuten oud is (volgende pagina's in hetzelfde tabblad);- u dwingt dit af met
data-rc-transport="http"(op een willekeurig element dat bij het opstarten in het document staat) of metwindow.ReactiveChatSettings = { transport: 'http' }vóór de loader.ReactiveChat('boot', { transport: 'http' })heeft geen effect — het verbindingstype wordt bij het opstarten vastgelegd.
Zonder fetch in de browser is er geen fallback. Als een bestaande WebSocket-verbinding wegvalt, maakt de widget via WebSocket opnieuw verbinding. Na een fallback controleert de widget eenmaal per pagina op de achtergrond of WebSocket nu wel doorkomt; zo ja, dan wordt rc_http verwijderd en gebruikt de volgende pagina weer WebSocket.
De verzoeken (onder https://ws.reactive.chat/rc_wss/h/):
- POST h/auf – opent de verbinding. Antwoord
{"v":1,"cid":"…","tok":"…","poll_ms":25000,"idle_ms":60000}.tokis een kort geldig verbindingstoken (32 tekens). - GET h/<cid>/sse?tok=… – Server-Sent Events,
id:endata:per frame, een keep-alive elke 15 s. Gemiste frames worden viaLast-Event-IDgeleverd. - GET h/<cid>/poll?tok=…&nach=<n> – long polling; de server houdt het verzoek maximaal 25 s open en antwoordt met
{"v":1,"n":…,"rahmen":[…]}. - POST h/<cid>/tx?tok=… – verzenden,
text/plain, één frame of een array van maximaal 20 frames; antwoord 204. - POST h/<cid>/zu?tok=… – sluiten (via
sendBeacon).
De widget probeert eerst SSE; als er binnen 5 seconden geen eerste frame aankomt (bijvoorbeeld door buffering in een proxy), wordt overgeschakeld op long polling. Fouten zoals 404 (unbekannt), 403, 413 of 429 worden behandeld als een weggevallen verbinding, waarna na een wachttijd opnieuw wordt geprobeerd. Na 60 seconden zonder verzoek sluit de server de verbinding. CORS: Access-Control-Allow-Origin neemt de origin over, zonder inloggegevens; de origincontrole vindt plaats in init, net als bij WebSocket.
Vereisten aan uw kant:
connect-srcvan uw CSP bevat het chatadres metwss://enhttps://.- Firewalls en proxy's staan verzoeken van minstens 30 seconden toe.
- Uw eigen proxy geeft
/rc_wss/zonder buffering door (nginxproxy_buffering offenproxy_cache off, Apacheflushpackets=on, Caddyflush_interval -1) en schrijft/rc_wss/h/niet naar het toegangslogboek (token in de query). Zie Eigen domein.
Opgeslagen in de browser: alleen de vermelding rc_http in sessionStorage met het tijdstip van de HTTPS-verbinding, zonder persoonsgegevens.
Welke route actief is
Op het netwerktabblad van de ontwikkelaarstools:
- WebSocket: een verbinding van het type WebSocket naar
wss://ws.reactive.chat/rc_wss/(of uw subdomein). In de frames ziet uinit,init_ok,ping/pongen de berichten. - HTTP-fallback: eerst
h/auf, daarna een langdurig…/sse-verzoek (of herhaalde…/poll-verzoeken) en één…/txvoor elk verzonden bericht.
Volgende: Limieten en quota · Problemen oplossen
آخرین بهروزرسانی: