Documentación para Desarrolladores

Conecte su propio software a su bandeja de SyBox

Envía mensajes de WhatsApp desde tu propio número, muestra los datos de tus clientes junto a cada chat, registra casos, recibe cada evento en tu propio servidor y pon un chat en vivo en tu sitio web. La ruta de envío usa el formato de la WhatsApp Cloud API, así que el código escrito para Meta solo necesita una nueva URL base y una clave de SyBox.

URL basehttps://api.sybox.io

Primeros pasos

Inicio Rápido

Tres cosas y su primer mensaje estará en camino: la URL base, una clave de dispositivo y el endpoint de mensajes.

POST/v26.0/{phone-number-id}/messagesAuthorization: Bearer <DEVICE_KEY>

El segmento de versión se acepta en cualquier formato v<n> (v26.0, v1.0, …) — mantenga el que ya usa su código. El {phone-number-id} es el ID de su número de WhatsApp, exactamente como en Meta.

El {phone-number-id} aparece después de id en la tarjeta del número, en Configuración › Mensajería › Canales. Una clave de envío está vinculada a un solo número y solo envía desde él.

cURL
curl -X POST https://api.sybox.io/v26.0/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "type": "text",
    "text": { "body": "Hello from SyBox" }
  }'

La tarjeta de cada clave en Configuración › Integraciones › Claves de API muestra cuándo se usó por última vez: la forma más rápida de ver si tus llamadas llegan.

Primeros pasos

Autenticación y claves

Cada llamada lleva una clave en el encabezado Authorization como token Bearer. Cada clave tiene una sola función: una clave creada para enviar no puede registrar un caso, y una clave creada para registrar casos no puede enviar. Guarda todas las claves en tu servidor; la única excepción es la clave pública del Chat en vivo, que está pensada para ir en tus páginas.

HTTP
Authorization: Bearer YOUR_KEY
Content-Type: application/json
ClavePara qué sirve
Enviar por WhatsAppPOST /v{n}/{phone-number-id}/messages
Registrar una reclamaciónPOST /v1/case
Iniciar una automatizaciónPOST /v1/automation
Leer y actualizar contactosPOST /v1/contacts/…
Recibir de un programaPOST /v1/inbound
Clave pública · lcpk_<script … data-key="lcpk_…">
Clave de firma para usuarios con sesión iniciada · lcid_Usuarios con sesión iniciada: la clave de firma
Clave de servidor (para tu propia pantalla de chat) · lcsk_/v1/livechat/*
Clave de firma de respuestas · lcwh_x-sybox-signature
Clave de firmaX-Hub-Signature-256

Primeros pasos

Obtener una clave de dispositivo

Las claves se crean dentro de su espacio de trabajo.

  1. Abra su espacio de trabajo → Configuración → Claves de API.
  2. Cada clave está vinculada a un número y se puede revocar en cualquier momento.
  3. Su token real de WhatsApp nunca sale de nuestro servidor.
  4. El gateway está desactivado por defecto.

API de WhatsApp

Envíe su primer mensaje

Un mensaje de texto plano. El cuerpo es el payload estándar de Cloud API.

POST/v26.0/{phone-number-id}/messagesAuthorization: Bearer <DEVICE_KEY>
curl -X POST https://api.sybox.io/v26.0/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "type": "text",
    "text": { "body": "Hello from SyBox" }
  }'

La Respuesta

Recibe exactamente lo que devuelve Meta, con el mismo estado HTTP.

200 OK · application/json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15551234567", "wa_id": "15551234567" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBI…" }]
}

API de WhatsApp

Cualquier tipo de mensaje funciona

Dado que el cuerpo se reenvía sin cambios, se admiten todos los tipos de mensajes de Cloud API.

{
  "messaging_product": "whatsapp",
  "to": "15551234567",
  "type": "template",
  "template": {
    "name": "order_shipped",
    "language": { "code": "en_US" },
    "components": [{
      "type": "body",
      "parameters": [{ "type": "text", "text": "#10432" }]
    }]
  }
}

El texto libre solo llega a un cliente dentro de las 24 horas siguientes a su último mensaje. Fuera de esa ventana, envía una plantilla aprobada: SyBox rechaza ahí el texto libre con el código 131047 antes de que llegue a Meta.

API de WhatsApp

Reglas y límites de las claves

Cada clave de envío tiene sus propias reglas, que defines al crearla: texto libre, plantillas o ambos; multimedia o no; qué plantillas; qué países de destino; las horas en que puede enviar; y un límite diario. SyBox rechaza un envío que incumpla alguna de ellas antes de que llegue a Meta.

EstadoCuándo
404Una respuesta vacía: la puerta de enlace de API está desactivada o la clave no pertenece a ningún espacio de trabajo. Revisa el interruptor de la parte superior de Configuración › Integraciones › Claves de API.
401Falta la clave o no se reconoce.
403La clave no es de envío, o envía desde un número al que no está vinculada.
403El mensaje incumple una de las reglas de la clave: texto libre, plantillas, multimedia, el nombre de la plantilla, el país o las horas.
403Meta ha pausado la plantilla o la ha limitado por su calificación de calidad.
400La ventana de 24 horas está cerrada (código 131047): envía una plantilla en su lugar.
400El cuerpo no es JSON válido, o el número de WhatsApp de la clave ya no está conectado.
409Meta ha impedido temporalmente que este número envíe mensajes.
429Se alcanzó el límite diario de la clave o el límite de mensajes de WhatsApp del número.
502No se pudo contactar con Meta. Inténtalo de nuevo.

El recuento diario se reinicia a medianoche UTC, y un envío que falla no cuenta.

API de WhatsApp

Errores

Los errores vienen de dos lugares, y ambos usan el formato de error de Meta, así que un cliente de la Cloud API existente los lee sin cambios. Un error de Meta llega tal como Meta lo envió, con el código de estado de Meta. Un error de una de las reglas propias de SyBox tiene el type SyBoxGatewayError, y su message indica qué regla rechazó la llamada.

{
  "error": {
    "message": "(#131030) Recipient phone number not in allowed list",
    "type": "OAuthException",
    "code": 131030
  }
}

Tu sistema en SyBox

Muestre sus datos junto al chat

Cuando un agente abre una conversación, SyBox llama a SU endpoint y muestra lo que devuelva junto al chat.

Añade tu endpoint en Configuración › Integraciones › APIs de ERP. Cada llamada es un POST con tu clave en Authorization y la solicitud en un solo campo, data, que contiene una cadena JSON; así cualquiera de los dos lados puede añadir campos más adelante sin romper el otro. Responde en menos de 15 segundos.

POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: lookup
X-Sybox-Lang: en
X-Sybox-Theme: light
X-Sybox-Agent: 6f1c…
X-Sybox-Number: 104857600000000
Content-Type: application/json

{ "data": "{\"phone\":\"15551234567\",\"email\":\"customer@example.com\",\"ref\":\"\"}" }

Cada llamada indica también quién pregunta: X-Sybox-Lang (el idioma del usuario; responde en él), X-Sybox-Theme (light o dark, para una respuesta html), X-Sybox-Agent (el id del usuario) y X-Sybox-Number (la línea en la que está la conversación).

Enviamos teléfono y correo — responda con { renderAs, content }.

renderAs

Establezca renderAs para indicar a SyBox cómo mostrar su contenido:

renderAsDescripción
tableFilas y columnas. content es un array de objetos planos (o { rows: [ … ] }); sus claves pasan a ser los encabezados de las columnas.
cards · kpiPequeños recuadros, como un saldo o un número de pedidos. content es un array de { label, value } (o { cards: [ … ] }).
feed · list · threadUna línea de tiempo de tarjetas. content es un array de { title, text, date, tags, media: [{ type, url, name }] }.
messageUn solo registro presentado como una tarjeta con aspecto de mensaje.
htmlTu propio HTML, como cadena en content. Solo para el marcado que generas tú mismo.
sectionsVarios bloques en una sola respuesta: content es { sections: [ { title, renderAs, content } ] }.
jsonEl valor predeterminado si omites renderAs: SyBox muestra tu content como JSON con formato.

Añada sybox_ref a cualquier fila de tabla para hacerla interactiva.

Búsqueda de productos (X-Sybox-Action: products)

El mismo punto de conexión también puede responder a búsquedas de productos. El usuario escribe lo que pide el cliente, SyBox lo envía como "q", y tu respuesta se muestra como tarjetas de producto junto al chat, con un botón de envío en un solo toque que coloca el enlace del producto en el editor.

POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: products

# the decoded "data":
{ "q": "wireless keyboard", "phone": "15551234567", "email": "customer@example.com", "ref": "" }
CampoDescripción
nameobligatorioEl único campo obligatorio. Un producto con solo un nombre sigue dando una ficha limpia.
price · oldPrice · currencyNúmeros, no cadenas con formato. Envíe oldPrice solo cuando exista un precio anterior real — el descuento se calcula a partir de ambos.
available · stockavailable es true/false; stock es un recuento opcional que se muestra al lado. Omítalos si no controla existencias — un campo ausente no dibuja nada y nunca se lee como «agotado».
imageUna URL de imagen https directa. Lo que no cargue deja un fondo neutro.
linkLa URL de la página del producto. Sin ella la ficha es de solo lectura — Enviar aparece solo cuando hay algo que enviar.
noteUna línea libre corta bajo el precio — plazo de entrega, variante, condición.

Nada más que el endpoint que ya construyó: misma URL, misma clave, mismo sobre { "data": "…" }. Use X-Sybox-Action para distinguir una búsqueda de producto de una consulta de cliente. Responda con content como un array (o { items: [ … ] }).

Tu sistema en SyBox

Abra un caso desde su propio sistema

Su sistema solicita a SyBox la apertura de un registro unificado de atención.

POST/v1/caseAuthorization: Bearer <CASE_KEY>
curl -X POST https://api.sybox.io/v1/case \
  -H "Authorization: Bearer $SYBOX_CASE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_unique_id": "CUST-10432",
    "header": "Screen replacement not delivered",
    "description": "Paid on 12 Aug, promised in 3 days, still nothing.",
    "department_id": 5,
    "ext_ref": "TICKET-99817"
  }'

Los campos

CampoDescripción
customer_unique_idTu propio id para el cliente: la misma clave que usa la sincronización de clientes. Si no tienes id, envía phone en su lugar.
phoneEl número de teléfono del cliente, si no tienes id.
header · descriptionSe requiere al menos uno. El encabezado es la línea que los usuarios ven en la lista.
department_idobligatoriointegerObligatorio. El departamento en el que se registra el caso: el número que aparece a su lado en Configuración › Departamentos (por ejemplo #5).
ext_refSu número de ticket.
sectionintegerDe 1 a 4: la pestaña de casos en la que va. Valor predeterminado: 1.
deadlinestringUna fecha límite, YYYY-MM-DD.
notifybooleanValor predeterminado true: aparece una nota con el número del caso en la conversación del cliente en SyBox. Al cliente no se le envía nada. Con false no se añade la nota.
sourcestringEl nombre del programa que se muestra en esa nota. Valor predeterminado: ERP.

La respuesta incluye el número del caso, el que tu equipo le indica al cliente. Un reintento con el mismo ext_ref responde "duplicate": true con el caso que ya existe, sin customer_id.

El cliente ya debe estar en SyBox. Si no se encuentra el id, SyBox hace una sincronización de clientes con tu sistema y vuelve a buscar; si sigue sin encontrarlo, la respuesta es 404: nunca se registra un caso a nombre de un cliente inventado.

Use una clave de tipo 'case'.

Tu sistema en SyBox

Transfiera mensajes desde otro programa

Para cualquier canal externo: notas de POS, alertas de dispositivos o registros heredados.

POST/v1/inboundAuthorization: Bearer <INBOUND_KEY>

Ya no se crean claves de este tipo: una clave de Recibir de un programa que ya tengas sigue funcionando. Para una integración nueva, registra un caso: SyBox escribe la nota en la conversación por ti.

cURL
curl -X POST https://api.sybox.io/v1/inbound \
  -H "Authorization: Bearer $SYBOX_INBOUND_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "15551234567",
    "text": "Order 4821 has left the warehouse.",
    "source": "Warehouse"
  }'
CampoDescripción
phoneobligatoriostringEl número de teléfono del cliente, con el código de país.
textobligatoriostringEl mensaje.
sourcestringEl nombre del programa, que se muestra en el mensaje.

La respuesta es {"ok": 1, "id": …}, el id de la nota en la conversación.

Solo entrada por diseño. Esta ruta escribe en la conversación.

Tu sistema en SyBox

Contactos: leer y actualizar

Con una clave del tipo Leer y actualizar contactos, un programa puede buscar un contacto, leerlo y cambiar su nombre, correo electrónico, campos de contacto y etiquetas. Cada llamada es un POST con un cuerpo JSON.

La clave actúa como la persona que la creó: solo encuentra los contactos que esa persona puede ver, y únicamente si tiene permiso para abrir la lista de contactos y ver los números. Para modificar cualquier dato, esa persona debe tener permiso para editar contactos. Si se desactiva al creador de la clave, esta deja de funcionar.

POST/v1/contacts/searchAuthorization: Bearer <CONTACTS_KEY>

Busca contactos cuyo nombre, teléfono o correo electrónico contenga q. limit es 20 por defecto y 50 como máximo.

POST/v1/contacts/get

Un contacto, por phone o por key (el mismo contact.key que envía un webhook).

POST/v1/contacts/update

Cambia solo lo que envías: name, email y fields (campos de contacto por id o por nombre, con sus valores; un valor vacío borra el campo).

POST/v1/contacts/labels

Añade y quita etiquetas por id o por nombre: add y remove son listas.

curl -X POST https://api.sybox.io/v1/contacts/update \
  -H "Authorization: Bearer $SYBOX_CONTACTS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "201112223344",
    "name": "Sara Ali",
    "fields": { "city": "Alexandria" }
  }'

# Find:   POST /v1/contacts/search  {"q": "sara", "limit": 20}
# Read:   POST /v1/contacts/get     {"phone": "201112223344"}
# Labels: POST /v1/contacts/labels  {"phone": "201112223344", "add": ["VIP"], "remove": [7]}

Cada llamada responde con el contacto (search: una lista de ellos): key, name, phone, email, channel, labels y fields.

Eventos

Webhooks: eventos en tu servidor

Añade tu endpoint en Configuración › Integraciones › Webhooks, elige los eventos y los números, y SyBox le envía cada evento por POST en cuanto ocurre. Un evento de WhatsApp llega en el propio formato de webhook de Meta, así que un controlador escrito para Meta funciona sin cambios.

EventoDescripción
messageMensaje entranteUn mensaje de un cliente.
statusEstado del mensajeEnviado, entregado, leído o fallido, para un mensaje que enviaste.
reactionReacciónUn cliente reaccionó a un mensaje.
doneConversación marcada como hechaUna conversación se marcó como hecha: por una persona, por una automatización o por sí sola tras el silencio del cliente.
labelEtiqueta añadida o quitadaSe añadió una etiqueta a una conversación o se quitó de ella.
contactContacto creado o actualizadoSe creó un contacto o cambiaron su nombre, su correo electrónico o sus campos de contacto.
dealTrato cerradoSe registró un trato cerrado en una conversación.
caseCaso CRM abierto o cerradoSe abrió o se cerró un caso CRM (resuelto, no resuelto o cancelado).
Solicitud · SyBox → tú
POST YOUR_WEBHOOK_URL
Content-Type: application/json
X-Hub-Signature-256: sha256=5d1f0c…
x-sybox-signature: sha256=5d1f0c…
x-sybox-event: message
x-sybox-timestamp: 1761400000

{
  "object": "whatsapp_business_account",
  "entry": [{
    "id": "WABA_ID",
    "changes": [{
      "field": "messages",
      "value": {
        "messaging_product": "whatsapp",
        "metadata": { "display_phone_number": "15550001111", "phone_number_id": "PHONE_NUMBER_ID" },
        "contacts": [{ "profile": { "name": "Sara" }, "wa_id": "15551234567" }],
        "messages": [{
          "from": "15551234567",
          "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQ…",
          "timestamp": "1761400000",
          "type": "text",
          "text": { "body": "Is my order shipped?" }
        }]
      }
    }]
  }]
}

Multimedia. El enlace propio de Meta necesita un token de Meta para abrirse, así que SyBox lo sustituye por un enlace firmado a su propia copia del archivo, válido durante 24 horas. Una nota de voz también puede incluir su texto en sybox.transcription, si el webhook lo solicita.

Otros canales (Messenger, Instagram, Telegram y Chat en vivo) llegan en el formato propio de SyBox, solo para el evento de mensaje:

JSON
{
  "object": "sybox",
  "event": "message",
  "channel": "telegram",
  "line": "8123456789",
  "workspace": "yourworkspace",
  "contact": { "key": "tg:6423115118", "name": "Sara" },
  "message": {
    "id": "812345",
    "timestamp": 1761400000,
    "type": "image",
    "text": "",
    "caption": "The broken part",
    "media": { "url": "https://api.sybox.io/…", "mime": "image/jpeg" }
  }
}

Los eventos de conversación llegan en el formato propio de SyBox para todos los canales. event indica exactamente qué ocurrió:

Evento<code>event</code>
Conversación marcada como hechaconversation.done
Etiqueta añadida o quitadalabel.added · label.removed
Contacto creado o actualizadocontact.created · contact.updated
Trato cerradodeal.closed
Caso CRM abierto o cerradocase.opened · case.closed
Evento de pruebatest
{
  "object": "sybox",
  "event": "conversation.done",
  "channel": "whatsapp",
  "line": "201001234567",
  "workspace": "yourworkspace",
  "timestamp": 1761400000,
  "contact": {
    "key": "201112223344",
    "name": "Sara",
    "phone": "201112223344",
    "email": "",
    "channel": "whatsapp",
    "labels": [{ "id": 3, "name": "VIP" }],
    "fields": [{ "id": 2, "key": "city", "name": "City", "value": "Cairo" }]
  },
  "data": { "by": "Omar", "note": "Refund sent" }
}

Clave de firma

EncabezadoDescripción
X-Hub-Signature-256sha256= y el HMAC-SHA256 en hexadecimal del cuerpo sin procesar, con el secreto de firma del webhook como clave: el mismo encabezado y el mismo método que usa Meta.
x-sybox-signatureEl mismo valor, para que un endpoint que también recibe las llamadas de Meta pueda distinguir las nuestras.
x-sybox-eventQué ocurrió: message, status, reaction, uno de los eventos de conversación de abajo (conversation.done, label.added…) o test.
x-sybox-timestampCuándo se envió, en segundos Unix.
x-sybox-signature-rsaUna segunda firma: rsa-sha256= y el RSA-SHA256 (PKCS#1 v1.5) en base64 de x-sybox-timestamp, un punto y el cuerpo sin procesar.
x-sybox-key-idCuál de nuestras claves la generó. La clave pública está en https://api.sybox.io/.well-known/sybox-webhook-key.

Comprueba la firma sobre los bytes sin procesar, antes de analizarlos, y compara en tiempo constante:

const crypto = require('crypto');

// express.raw: the signature is over the bytes as sent, so read them before any JSON parser.
app.post('/sybox/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sent = Buffer.from(req.get('X-Hub-Signature-256') || '');
  const want = Buffer.from('sha256=' + crypto
    .createHmac('sha256', process.env.SYBOX_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex'));
  if (sent.length !== want.length || !crypto.timingSafeEqual(sent, want)) return res.sendStatus(401);

  res.sendStatus(200);                       // answer first, then do the work
  const event = JSON.parse(req.body);
  const kind = req.get('x-sybox-event');     // message | status | reaction
});

¿Prefieres no guardar un secreto compartido en tu servidor? Comprueba x-sybox-signature-rsa con nuestra clave pública. En ambos casos, rechaza una marca de tiempo de más de cinco minutos de antigüedad.

Responde con cualquier 2xx en menos de 15 segundos. Cualquier otra cosa (un error, un tiempo de espera agotado, una redirección) se reintenta después de 1, 2, 4, 8, 16 y 32 minutos, y luego se descarta. Un evento puede llegar más de una vez: usa el id del mensaje para ignorar una repetición. La dirección debe ser pública y https.

Un webhook que falla 20 veces seguidas durante más de una hora se desactiva automáticamente y se avisa al administrador del espacio de trabajo. Sus eventos pendientes se conservan durante 24 horas: vuelve a activarlo para enviarlos.

Para probar, pulsa Enviar evento de prueba en la tarjeta del webhook. Entregas, en la misma tarjeta, muestra las últimas 50 entregas con la respuesta de tu servidor y permite enviar una de nuevo.

Chat en vivo

Chat en vivo

Pon un chat en tu sitio web: los visitantes escriben allí y tu equipo responde en SyBox. Añade el sitio web en Configuración › Mensajería › Canales, en la tarjeta Chat en vivo; la ventana que se abre, Código para tu sitio, te da el código y las claves de abajo. Las tres claves secretas se muestran una sola vez.

ClaveEmpieza porPara qué sirve
Clave públicalcpk_Va en tus páginas. Puedes publicarla sin riesgo: solo identifica el sitio web, y el chat solo se muestra en tus Direcciones de sitios web.
Clave de firma para usuarios con sesión iniciadalcid_Solo en tu servidor. Firma a cada usuario con sesión iniciada, para que nadie pueda escribir haciéndose pasar por otra persona.
Clave de servidor (para tu propia pantalla de chat)lcsk_Solo en tu servidor. Solo para un sitio que crea su propia pantalla de chat en lugar de usar el chat listo para usar.
Clave de firma de respuestaslcwh_Solo en tu servidor. Demuestra que una respuesta enviada a tu servidor vino de SyBox.

El chat listo para usar solo necesita el código. La clave de firma es para un sitio con cuentas; la clave de servidor y la clave de firma de respuestas, solo para un sitio con su propia pantalla de chat.

Al desactivar un sitio web, el chat se oculta de inmediato. Las llamadas de tu servidor se siguen aceptando y guardando.

Añade el chat a tu sitio

Pega el código una vez en cada página, justo antes de la etiqueta de cierre de body. El chat solo se muestra en las direcciones de Direcciones de sitios web, y su color, posición, título y bienvenida se configuran en SyBox, no en el código.

HTML
<script async src="https://api.sybox.io/lc/w.js" data-key="lcpk_YOUR_PUBLIC_KEY"></script>

WordPress: descarga nuestro plugin desde la misma ventana. Shopify: en la app de Shopify de SyBox, activa Chat en vivo en esta tienda y luego SyBox Live Chat en App embeds; un cliente con sesión iniciada se reconoce sin que tengas que hacer nada más.

Usuarios con sesión iniciada: la clave de firma

Cuando un usuario ha iniciado sesión en tu sitio, tu servidor le dice al chat quién es con un token corto (un JWT, HS256) firmado con la clave de firma. Así el usuario tiene una sola conversación a su nombre, en todos sus dispositivos, y nadie puede abrirla escribiendo el id de otra persona.

  1. Después del inicio de sesión, tu servidor crea el token con los claims de abajo.
  2. La página se lo pasa al chat como identity, antes del código del chat.
  3. Tras un inicio de sesión sin recargar la página, llama a SyBoxChat('identify', token) en su lugar.
CampoDescripción
subobligatoriostringEl id de tu usuario, de hasta 120 caracteres. No puede empezar por v_.
expobligatorionumberCaducidad, en segundos Unix: como máximo un día después. Diez minutos es lo adecuado: el token solo se lee cuando se abre el chat.
namestringEl nombre que muestra la conversación.
emailstringLa dirección de correo electrónico del usuario.
phonestringEl número de teléfono del usuario.
picturestringUna dirección https de la foto del usuario, de hasta 500 caracteres. SyBox guarda su propia copia y la vuelve a descargar en el siguiente inicio de sesión cuando la dirección cambia.
function b64url(string $s): string { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); }

$header  = b64url(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$payload = b64url(json_encode([
    'sub'   => (string) $user->id,
    'name'  => $user->name,
    'email' => $user->email,
    'exp'   => time() + 600,          // ten minutes
]));
$signature = b64url(hash_hmac('sha256', "$header.$payload", getenv('SYBOX_LC_SIGNING_KEY'), true));
$token = "$header.$payload.$signature";
HTML
<script>
  window.SyBoxChatConfig = { identity: "TOKEN_FROM_YOUR_SERVER", locale: "en" };
</script>
<script async src="https://api.sybox.io/lc/w.js" data-key="lcpk_YOUR_PUBLIC_KEY"></script>

Crea el token solo en tu servidor. Una clave de firma en una página web permite a cualquiera escribir como cualquiera de tus usuarios. Y nunca pongas en la página un id de usuario sin más en lugar del token: eso permitiría a cualquiera elegir qué conversación leer.

Con Solo usuarios con sesión iniciada en mi sitio activado, un visitante sin token no ve el chat.

A un visitante que chateó antes de iniciar sesión se le pregunta una vez, en el mismo navegador, si quiere continuar esa conversación con su cuenta. No tienes que hacer nada para esto.

Controla el chat desde tu página

El código del chat añade una función a tu página, SyBoxChat:

JavaScript
SyBoxChat('open');                       // open the chat
SyBoxChat('close');                      // close it
SyBoxChat('identify', token);            // after a sign-in without a page reload
SyBoxChat('locale', 'fr');               // the chat's language
SyBoxChat('on', 'message', function (m) {
  // a reply arrived
});
SyBoxChat('on', 'open', function () { /* the chat opened */ });
SyBoxChat('on', 'close', function () { /* the chat closed */ });

// Before the chat's code, every setting is optional:
window.SyBoxChatConfig = {
  identity: 'TOKEN_FROM_YOUR_SERVER',    // a signed-in user (see above)
  name: 'Sara', email: 'sara@example.com', // a visitor without an account: fills the form for them
  locale: 'ar',
  serviceWorker: 'auto'                   // 'auto' | '/your-sw.js' | 'site' | 'off'
};

Notificaciones después de cerrar tu sitio

Mientras haya una página de tu sitio abierta, una respuesta se muestra como notificación por sí sola. Para llegar a un visitante después de que cierre tu sitio, los navegadores exigen un service worker en tu propio dominio: sirve esta única línea como /sybox-lc-sw.js en la raíz de tu sitio, como application/javascript.

/sybox-lc-sw.js
try { importScripts('https://api.sybox.io/lc/sw.js'); } catch (e) {}

Elige el modo en SyBox (Notificaciones con el sitio web cerrado) o en la página con serviceWorker: "auto" (el archivo de arriba, el valor predeterminado), una ruta propia, "site" (pon la misma línea al principio de tu propio service worker) o "off".

El chat nunca reemplaza un service worker que tu sitio ya tenga, y el archivo no tiene controlador fetch, así que nunca ve ninguna solicitud de tu sitio. El plugin de WordPress sirve el archivo por ti. Safari en iPhone no muestra notificaciones de un sitio web normal.

Tu propia pantalla de chat: la clave de servidor

Un sitio o una app con su propia pantalla de chat se comunica con SyBox desde su servidor en lugar de usar el chat listo para usar. Cada llamada lleva Authorization: Bearer lcsk_…. Tu servidor identifica al usuario con tu propio id, y SyBox confía en él porque tiene la clave.

LlamadaDescripción
POST /v1/livechat/messagesTu usuario escribe. client_id hace que un reintento sea seguro: el mismo id se guarda una sola vez ("duplicate": 1).
POST /v1/livechat/uploadUn archivo, como datos de formulario multipart: file, user_id, client_id. Hasta 16 MB: imágenes, voz, video, PDF, texto y documentos de Office.
GET /v1/livechat/messages?user_id=&since=&before=&limit=La conversación: los mensajes posteriores a since, y agents_read_id, el más reciente de los mensajes de tu usuario que ha leído un agente.
POST /v1/livechat/readTu usuario ha leído las respuestas hasta up_to.
POST /v1/livechat/editTu usuario edita su propio mensaje, en un plazo de 15 minutos.
POST /v1/livechat/deleteTu usuario elimina su propio mensaje para todos, en un plazo de 24 horas.
POST /v1/livechat/reactUna reacción en cualquier mensaje; un emoji vacío la quita.
POST /v1/livechat/typingTu usuario está escribiendo: envía on: 1 cada pocos segundos y on: 0 cuando deje de escribir.
curl -X POST https://api.sybox.io/v1/livechat/messages \
  -H "Authorization: Bearer $SYBOX_LC_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user":    { "id": "u42", "name": "Sara Ali", "email": "sara@example.com", "locale": "ar" },
    "message": { "client_id": "a1b2c3", "text": "Hi, is my order on its way?", "reply_to": "" }
  }'

Un mensaje, en el historial y en las respuestas:

JSON
{
  "id": 990,
  "guid": "b81d…",
  "dir": "out",                       // "in" = your user wrote it, "out" = your team
  "type": "image",                    // text | image | audio | video | file
  "text": "Here is the tracking page",
  "media": { "url": "https://api.sybox.io/…", "mime": "image/png", "name": "tracking.png", "w": 1080, "h": 720 },
                                       // a voice note or a video also has "secs"
  "status": 3,                         // on your user's messages: 1 received, 3 read by an agent
  "reply_to": "7c9e…",
  "client": "a1b2c3",                  // on your user's messages: the client_id you sent
  "react_us": "",                      // your team's reaction on it
  "react_me": "👍",                    // your user's reaction on it
  "at": "2026-10-25T09:41:07Z",
  "edited": 0
}

El historial de un usuario que nunca ha escrito responde 404 unknown_user: muestra una conversación vacía.

Los enlaces de multimedia están firmados y funcionan durante una hora; vuelve a pedirlos para obtener uno nuevo. SyBox nunca descarga un archivo desde una dirección que envíes: súbelo. Hasta 120 llamadas por minuto por sitio web.

Respuestas en tu servidor: la clave de firma de respuestas

Introduce Dirección de tu servidor para respuestas en el sitio web. Cuando tu equipo responde, edita, elimina o lee, SyBox envía el evento por POST a esa dirección, firmado con la clave de firma de respuestas.

EncabezadoDescripción
x-sybox-signaturet=TIMESTAMP,v1=SIGNATURE: TIMESTAMP en segundos Unix y SIGNATURE el HMAC-SHA256 en hexadecimal de TIMESTAMP.BODY (el cuerpo sin procesar), con la clave de firma de respuestas como clave.
x-sybox-eventmessage.created, message.updated, message.deleted o messages.read.
x-sybox-deliveryEl mismo en cada reintento de un evento: úsalo para ignorar una repetición.

El cuerpo contiene event, line_id, user_id y message, o message_id en una eliminación y read_id en messages.read: los mensajes de tu usuario están leídos hasta ese id.

const crypto = require('crypto');

app.post('/sybox/livechat', express.raw({ type: 'application/json' }), (req, res) => {
  const parts = Object.fromEntries((req.get('x-sybox-signature') || '').split(',').map(p => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(401);   // older than five minutes

  const want = Buffer.from(crypto.createHmac('sha256', process.env.SYBOX_LC_REPLY_KEY)
    .update(t + '.').update(req.body).digest('hex'));
  const sent = Buffer.from(parts.v1 || '');
  if (sent.length !== want.length || !crypto.timingSafeEqual(sent, want)) return res.sendStatus(401);

  res.sendStatus(200);
  const delivery = req.get('x-sybox-delivery');   // seen it already? ignore
  const evt = JSON.parse(req.body);               // { event, line_id, user_id, message | message_id | read_id }
});

Comprueba la firma sobre el cuerpo sin procesar, rechaza un t de más de cinco minutos de antigüedad y responde con 2xx rápidamente. Cualquier otra cosa se reintenta después de 10 s, 30 s, 2 min, 10 min y 30 min, y luego se descarta. SyBox solo llama a direcciones públicas y nunca sigue una redirección.

Códigos de motivo

Cada respuesta del Chat en vivo es JSON: {"ok": 1, …}, o {"ok": 0, "error": "CODE"} con un código en lugar de una frase:

CódigoEstadoDescripción
unauthorized401La clave de servidor es incorrecta o se ha reemplazado.
invalid_json invalid_payload invalid_user_id400La solicitud no tiene el formato esperado.
empty_message no_file empty_file400No hay texto ni archivo.
too_long400El texto editado supera los 4096 caracteres.
not_an_emoji400emoji no es un emoji.
not_found404El mensaje no existe o no es de este usuario.
too_late409Ya pasó el plazo para editar o eliminar.
file_type_not_allowed400No se acepta este tipo de archivo.
file_too_large413El archivo supera los 16 MB.
blocked403Tu equipo ha bloqueado a este usuario.
unknown_user404Todavía no hay ninguna conversación para este id de usuario.
rate_limited429Demasiadas llamadas: ve más despacio e inténtalo de nuevo.
maintenance503SyBox se está actualizando: inténtalo de nuevo después de Retry-After segundos.
server_error500Algo falló de nuestro lado.

Reemplazar una clave

¿Perdiste una clave o crees que se filtró? En la tarjeta del sitio web, haz clic en Nueva clave de firma, Nueva clave de servidor o Nueva clave de firma de respuestas. Una nueva clave de servidor deja la anterior funcionando durante un día, para que puedas hacer el cambio sin interrupciones; las otras claves dejan de funcionar de inmediato.