← docs

Contrato de eventos v1

El corazón del SDK. Solo cambios aditivos: agregar campos opcionales está permitido; quitar, renombrar o cambiar tipos exige un v2 conviviendo con v1. Tipos de evento desconocidos se IGNORAN (forward-compat), así que tu juego nunca se rompe por un evento nuevo.

Envelope

Todo mensaje, por cualquier transporte, es un JSON con este sobre:

{
  "v": 1,
  "id": "evt_01J9ZK3M7Q",
  "type": "chat",
  "ts": 1751990000000,
  "data": { }
}
CampoTipoDescripción
vnumberVersión del contrato. Siempre 1.
idstringId único del evento (usa esto para deduplicar si reconectas).
typestringTipo de evento. Tipos desconocidos se IGNORAN.
tsnumberEpoch ms en que el agente capturó el evento.
dataobjectPayload según tipo.

Objeto user (común a los eventos de viewer)

{
  "userId": "6829385920134",
  "username": "pepito_gamer",
  "displayName": "Pepito 🎮",
  "avatarUrl": "https://p16-sign.tiktokcdn..."
}

username es el uniqueId de TikTok (estable, sin @). avatarUrl puede venir null.

Eventos de live

chat — mensaje en el chat

{ "v": 1, "id": "evt_x1", "type": "chat", "ts": 1751990000000,
  "data": { "user": { "...": "..." }, "message": "JOIN" } }

gift — regalo recibido

{ "v": 1, "id": "evt_x2", "type": "gift", "ts": 1751990001000,
  "data": {
    "user": { "...": "..." },
    "giftId": 5655, "giftName": "Rose", "diamonds": 1,
    "count": 3, "streakEnd": true,
    "imageUrl": "https://..."
  } }

count es la cantidad acumulada del streak; streakEnd: true indica que el streak cerró. Para gifts apilables, cuenta SOLO cuando streakEnd es true. En v1 los gifts llegan SIEMPRE con streakEnd: true (el agente resuelve el streak antes de emitir). La intensidad de un gift es diamonds * count.

join — viewer entra al live

{ "data": { "user": { "...": "..." } } }

like — likes (agrupados)

{ "data": { "user": { "...": "..." }, "count": 15, "totalLikes": 5230 } }

follow — nuevo follower durante el live

{ "data": { "user": { "...": "..." } } }

share — viewer comparte el live

{ "data": { "user": { "...": "..." } } }

live_status — ciclo de vida del live

{ "data": { "status": "started", "roomId": "7301234..." } }

status es "started" o "ended".

Mensajes de control (del sistema al juego)

init — primer mensaje al conectar

{ "v": 1, "type": "init", "ts": 1751990000000,
  "data": {
    "app": { "appId": "com.dev.batalla-clanes", "version": "1.2.0" },
    "live": { "active": true, "roomId": "7301234...", "host": "vureta" },
    "config": { "comando_join": "JOIN", "vidas_iniciales": 3, "gift_revive": 5655 }
  } }

config llega RESUELTA: los valores que el streamer guardó, con los defaults del manifest para lo no configurado.

config_updated — hot-reload de config

{ "v": 1, "type": "config_updated", "ts": 1751990500000,
  "data": { "config": { "comando_join": "UNIRME", "vidas_iniciales": 5 } } }

Se emite cuando el streamer guarda cambios con el juego conectado. Llega la config COMPLETA (no un diff).

ping / pong

En WebSocket el servidor envía {"v":1,"type":"ping"} cada 30 s; responde {"v":1,"type":"pong"}. El long-poll no lo necesita.

Garantías y no-garantías

  • Orden: en orden de captura por transporte local; por relay cloud se preserva dentro del cursor.
  • Entrega: at-most-once en WebSocket (si te desconectas pierdes lo emitido mientras tanto); long-poll y relay tienen cursor con buffer corto (~60 s) para no perder ráfagas.
  • Sin persistencia: el canal no es historial. Si necesitas acumular, acumula tú.
  • Rate: sin throttling en v1. Un live grande puede emitir decenas de eventos/s; procesa barato o encola internamente.

Soporte inicial

La captura actual soporta desde el día uno: chat, gift, join, like, follow y live_status. Aún no se emiten share ni like.data.totalLikes; user.avatarUrl puede venir null. Como los tipos ausentes simplemente no llegan y el cliente ignora tipos desconocidos, agregar soporte después es 100% compatible.