Transporte: WebSocket, alternativa HTTP y reconexión
Cómo se comunica el widget con el servidor de chat: direcciones, subprotocolo rc.v1, formato de tramas, códigos de error y cierre, límites, reconexión con seq y deduplicación, y alternativa HTTP mediante Server-Sent Events y sondeo largo.
Esta página describe lo que ocurre entre el widget del navegador y el servidor de chat. Le servirá para resolver problemas (pestaña de red de las herramientas de desarrollo), configurar reglas de proxy y cortafuegos y definir su Content Security Policy.
Importante: el protocolo de comunicación es la interfaz entre nuestro widget y nuestro servidor. Los clientes propios que utilizan directamente el protocolo no cuentan con soporte oficial; los campos pueden ampliarse dentro de la versión 1 del protocolo. Para sus propias interfaces, utilice la API JavaScript; para conexiones con servidores, los webhooks y la API de servidor.
Direcciones
- WebSocket:
wss://ws.reactive.chat/rc_wss/ - Alternativa HTTP, formulario sin conexión, cargas: la misma dirección con
https://, es decir,https://ws.reactive.chat/rc_wss/h/…,https://ws.reactive.chat/api/offline/<SITE_KEY>yhttps://ws.reactive.chat/rc_wss/upload/… - Archivos del widget:
https://cdn.reactive.chat/
Cómo encuentra el widget la dirección WebSocket:
data-rc-ws-urlen la etiqueta del cargador,window.ReactiveChatSettings.wsUrloReactiveChat('boot', { wsUrl }): si se establece, se aplica ese valor.- En caso contrario, se deduce de la ubicación de
loader.js:wss://ws.+ los dos últimos componentes del dominio +/rc_wss/.cdn.reactive.chatse convierte enwss://ws.reactive.chat/rc_wss/.
Esto conlleva dos posibles problemas:
- Si sirve
loader.jsdesde su dominio propio, el widget deduce una dirección bajo su dominio. En ese caso, establezcadata-rc-ws-url(véase Inicio rápido). - Con terminaciones de dos partes, como
.co.uk, la deducción es incorrecta. Establezcadata-rc-ws-urlexpresamente.
La ruta WebSocket no evalúa parámetros de consulta.
Establecer la conexión
- Momento: el widget no se conecta al cargar la página, sino con la primera interacción real (movimiento del ratón, clic, pulsación de tecla, rueda del ratón, toque, foco). Se conecta inmediatamente si la ventana del chat está abierta o se está abriendo, si se conoce una conversación del visitante o si hay mensajes pendientes de envío. Con
data-rc-connect="load"en cualquier elemento (normalmente, la etiqueta del cargador) owindow.ReactiveChatSettings.connect = 'load', se conecta al cargar, como antes. Las reglas que se activan cierto tiempo después de ver la página cuentan a partir de la primera interacción. - Subprotocolo: el widget abre
new WebSocket(url, 'rc.v1')y el servidor devuelverc.v1. La versión del protocolo figura en cada trama (v: 1), no en la dirección. - Solo tramas de texto, en UTF-8; una trama = un mensaje. Sin compresión (
permessage-deflateestá desactivado en el servidor). - Negociación inicial: tras
open, el widget envíainiten un máximo de 5 segundos; el servidor responde coninit_ok. Siopenno llega en 8 segundos, el intento se considera fallido; siinit_okno llega en 10 segundos, el widget vuelve a conectarse. - Comprobación del origen: el servidor compara el nombre de host de la cabecera
Origincon los dominios del proyecto (Orígenes permitidos en su cuenta). El esquema y el puerto no cuentan; se admiten subdominios (www.your-company.comcoincide conyour-company.com;notyour-company.comno). Si falla: errororigin_denied, seguido de cierre 1008, sin reintento. Si no se ha introducido ningún dominio en el proyecto, no se comprueba el origen; por eso debe introducir siempre sus dominios.
Formato de las tramas
Todos los mensajes en ambos sentidos tienen esta forma:
{ "v": 1, "t": "msg", "id": "01JAB3C4D5E6F7G8H9JKMNPQRS", "ts": 1792500000000, "d": { } }
- v (entero, obligatorio): versión del protocolo, actualmente
1. - t (cadena, obligatorio): tipo de mensaje,
[a-z0-9_]{1,24}. - id (cadena, obligatorio): ULID de 26 caracteres en Crockford Base32, solo mayúsculas. También es la clave de deduplicación.
- ts (entero, obligatorio): milisegundos desde 1970, solo para diagnóstico.
- d (objeto, obligatorio): datos de la trama, nunca
null;{}cuando está vacío.
Los tipos y campos desconocidos se ignoran sin aviso; esto mantiene la compatibilidad de los widgets antiguos con servidores más recientes.
Resumen de los tipos de mensaje
Del widget al servidor:
- init: registro de la conexión: clave del sitio, identificadores de sesión, visitante, pestaña y conversación, idioma, estado del consentimiento, la identidad firmada, si existe (
user), contexto de la página (ctx) y punto de reanudación (resume). - msg: mensaje del visitante (texto, hasta 5 archivos adjuntos, respuesta a respuestas rápidas o formularios).
- typing: indicador de escritura (
start/stop). El contenido del campo de entrada nunca se transmite. - read: leído hasta
seq. - ctx: cambio de contexto, por ejemplo, al navegar en una SPA o después de
identify. - csat: valoración (pulgar arriba/abajo, comentario de hasta 1000 caracteres).
- upload_req: solicitud de autorización de carga (nombre, tipo MIME, tamaño, SHA-256).
- transcript, forget, consent: conversación por correo, eliminación de los propios datos, otorgamiento/revocación del consentimiento.
- ping: señal de mantenimiento de la conexión.
Del servidor al widget:
- init_ok: identificadores confirmados, historial, diseño, funciones y límites (véase más abajo), resultado de la verificación de identidad (
identity.verified). - msg: mensajes (incluido el eco del propio mensaje del visitante con
client_id). - bot_typing, bot_stream: el asistente de IA está pensando o escribiendo;
bot_streamtransmite siempre todo el texto visible hasta ese momento, como máximo cada 150 ms. - typing, read, handoff, queue_pos, proactive, conv_list, error, pong.
Códigos de error
Un error llega como una trama t: "error" con d.code, d.message (en inglés, solo para desarrolladores), d.retry (no, immediate, backoff, after_reinit) y, cuando es necesario, d.retry_after_ms y d.detail. La lista es cerrada; un código desconocido se trata como internal.
- bad_json: JSON no válido; después de tres casos, cierre 1008.
- bad_envelope: la trama incumple el formato (también si el ULID se desvía más de 24 h,
detail.reason: stale_or_future). - unsupported_version: no se puede negociar la versión; cierre 4001.
- unknown_site_key: clave del sitio desconocida, proyecto pausado o archivado; cierre 4002.
- origin_denied: el origen no pertenece a los dominios del proyecto; cierre 1008.
- visitor_blocked: visitante bloqueado; cierre 1008.
- auth_failed: verificación de identidad fallida,
detail.reason:no_hash,bad_hash,bad_exp,expired,exp_too_far. La sesión continúa de forma anónima. - not_initialized / already_initialized: mensaje antes de
inito un segundoinit. - init_timeout: no se recibe
initen 5 s; cierre 4004. - conversation_not_found / conversation_closed
- rate_limited: límite de solicitudes; con
retry_after_ms. Si se repite en la misma conexión, el servidor la cierra con 4008. - payload_too_large: trama de más de 64 KiB.
- msg_too_long: texto que supera el límite del plan,
detail.max. - unsupported_kind, attachment_invalid
- upload_disabled, upload_too_large, upload_mime_denied, upload_expired
- consent_required: la acción requiere consentimiento.
- dnd_active: «No molestar» está activo.
- session_conflict: demasiadas conexiones de la misma sesión (más de 3); cierre 4003, la pestaña pasa a ser seguidora.
- internal, unavailable: temporales; reintento tras una espera.
Códigos de cierre
- 1000, 1001, 1006, 1011, 1013, 4004: el widget vuelve a conectarse (tras una espera).
- 1008: origen rechazado, JSON no válido repetidamente, bloqueo por la protección contra abusos. Sin reintento en esta página.
- 4001: no se puede negociar la versión. Sin reintento.
- 4002: clave del sitio desconocida o proyecto inactivo. Sin reintento.
- 4008: limitación de solicitudes repetida. El widget espera
retry_after_msy vuelve a conectarse. - 4003: conflicto de sesión; la pestaña funciona como seguidora de otra pestaña.
Límites por conexión
El servidor envía los valores vinculantes en init_ok.limits e init_ok.features:
- heartbeat_ms 25 000: el widget envía un
ping25 s después del último mensaje saliente. - pong_timeout_ms 10 000: dos
pongsin recibir provocan una reconexión. - idle_timeout_ms 70 000: el servidor cierra tras 70 s sin recibir una trama (1001).
- max_frame_bytes 65 536: las tramas mayores generan
payload_too_large. - msg_per_10s 20 y frames_per_10s 120: depósito de tokens por conexión; al superarlo,
rate_limited. - reconnect_cap_ms 30 000: espera máxima entre reintentos.
- dedup_window_ms 86 400 000: 24 horas de deduplicación.
- features.max_msg_len: caracteres (puntos de código Unicode) por mensaje del visitante: 1000 en el plan Free, 4000 en los planes de pago. El widget no envía textos demasiado largos.
- features.max_upload_bytes, features.allowed_mime: véase Límites y cuotas.
Reconexión, orden y duplicados
- Espera:
min(30000, 500 × 2^(attempt−1))ms; se utiliza un valor aleatorio de la mitad superior del intervalo (intento 1: 250–500 ms; desde el intento 7: 15–30 s). En pestañas en segundo plano, el triple. Elretry_after_msdel servidor es el límite inferior. El número de intentos es ilimitado; el contador solo se reinicia coninit_ok. Si el navegador notificaonline, el widget lo intenta de inmediato; si está sin conexión, espera. - Orden: cada mensaje de una conversación lleva un
seqconsecutivo sin saltos, empezando por 1. Los mensajes se muestran porseq, no por hora. Al reconectarse, el widget envíaresume.last_seqy hasta 50 identificadores pendientes; el servidor entrega todo lo posterior (un máximo de 100 mensajes; en caso contrario,has_more) y repite las confirmaciones de los mensajes que ya han llegado. - Sin duplicados: el servidor recuerda cada
idde mensaje durante 24 horas. Un mensaje repetido no se vuelve a almacenar, sino que recibe la misma confirmación. «Reenviar» en el widget utiliza el mismoid. - Estados de un mensaje saliente:
pending→sent→delivered(eco del servidor) →read;failedtras 5 intentos, después de 24 h o conretry: "no". - Búfer sin conexión: si se interrumpe la conexión, los mensajes pasan a un búfer (máximo 50 entradas, 128 KiB, 24 h): con consentimiento, en
localStorage; sin él, ensessionStorage. Se envían en el siguienteinit_ok. - Varias pestañas, una conexión: las pestañas de un sitio eligen una líder (Web Locks o, en su defecto, una señal de mantenimiento en
localStorage); solo la líder mantiene la conexión y las demás se comunican medianteBroadcastChannel.
Alternativa HTTP
Si una red no permite pasar un WebSocket, el chat continúa mediante solicitudes HTTPS normales, con las mismas tramas y los mismos límites. Solo la utiliza el widget; la interfaz de agentes siempre se conecta por WebSocket. La versión breve para editores está en Alternativa HTTP.
Cuándo se aplica:
- el navegador no admite WebSocket;
- dos intentos consecutivos de WebSocket fallan antes de que se haya establecido ningún WebSocket en esta página (un intento sin resultado tras 8 segundos se considera fallido);
sessionStoragecontiene una entradarc_httpde menos de 30 minutos de antigüedad (páginas posteriores en la misma pestaña);- la fuerza con
data-rc-transport="http"(en cualquier elemento presente en el documento al arrancar) o conwindow.ReactiveChatSettings = { transport: 'http' }antes del cargador.ReactiveChat('boot', { transport: 'http' })no tiene efecto: el tipo de conexión se fija al arrancar.
Sin fetch en el navegador no hay alternativa. Si se interrumpe una conexión WebSocket existente, el widget vuelve a conectarse mediante WebSocket. Tras pasar a la alternativa, comprueba discretamente una vez por página si ya puede pasar un WebSocket; si lo consigue, elimina rc_http y la siguiente página vuelve a utilizar WebSocket.
Las solicitudes (bajo https://ws.reactive.chat/rc_wss/h/):
- POST h/auf: abre la conexión. Respuesta
{"v":1,"cid":"…","tok":"…","poll_ms":25000,"idle_ms":60000}.tokes un token de conexión de corta duración (32 caracteres). - GET h/<cid>/sse?tok=…: Server-Sent Events,
id:ydata:por trama, señal de mantenimiento cada 15 s. Las tramas perdidas se entregan medianteLast-Event-ID. - GET h/<cid>/poll?tok=…&nach=<n>: sondeo largo; el servidor mantiene abierta la solicitud hasta 25 s y responde con
{"v":1,"n":…,"rahmen":[…]}. - POST h/<cid>/tx?tok=…: enviar,
text/plain, una trama o un array de un máximo de 20 tramas; respuesta 204. - POST h/<cid>/zu?tok=…: cerrar (mediante
sendBeacon).
El widget prueba primero SSE; si no llega la primera trama en 5 segundos (por ejemplo, porque un proxy la almacena en búfer), pasa al sondeo largo. Trata errores como 404 (unbekannt), 403, 413 o 429 como una interrupción de la conexión y reintenta tras una espera. Después de 60 segundos sin solicitudes, el servidor cierra la conexión. CORS: Access-Control-Allow-Origin refleja el origen, sin credenciales; el origen se comprueba en init, igual que con WebSocket.
Requisitos de su lado:
- El
connect-srcde su CSP contiene la dirección del chat conwss://yhttps://. - Los cortafuegos y proxies permiten solicitudes que duren al menos 30 segundos.
- Su proxy propio transmite
/rc_wss/sin almacenamiento en búfer (nginxproxy_buffering offyproxy_cache off, Apacheflushpackets=on, Caddyflush_interval -1) y no escribe/rc_wss/h/en el registro de acceso (token en la consulta). Véase Dominio propio.
Almacenado en el navegador: solo la entrada rc_http en sessionStorage con el momento de establecimiento de la conexión HTTPS, sin datos personales.
Qué ruta está activa
En la pestaña de red de las herramientas de desarrollo:
- WebSocket: una conexión de tipo WebSocket a
wss://ws.reactive.chat/rc_wss/(o su subdominio). En las tramas veráinit,init_ok,ping/pongy los mensajes. - Alternativa HTTP: primero
h/auf, después una…/ssede larga duración (o…/pollrecurrentes) y una…/txpor cada mensaje enviado.
Siguiente: Límites y cuotas · Resolución de problemas
Utolsó frissítés: