Manifest v1
El manifest es lo único que entregas a la plataforma (además de tu listing). Declara identidad, plataforma, transporte, eventos que consumes y el schema de configuración que el streamer verá renderizado en el portal. Se valida en el servidor al publicar; el wizard te muestra los mismos errores EN VIVO mientras editas.
Ejemplo completo
{
"manifestVersion": 1,
"appId": "com.pepitodev.batalla-clanes",
"name": "Batalla de Clanes",
"version": "1.2.0",
"platform": "gtasa",
"transport": "local-poll",
"events": ["chat", "gift", "join"],
"listing": {
"title": "Batalla de Clanes para GTA San Andreas",
"shortDescription": "Tus viewers pelean en bandos escribiendo JOIN en el chat",
"description": "Markdown largo con instrucciones de instalación…",
"coverImage": "cover.png",
"videos": ["https://www.youtube.com/watch?v=XXXX"],
"downloadUrl": "https://github.com/pepitodev/batalla-clanes/releases/latest",
"categoryIds": ["<id-de-categoria>"]
},
"config": [
{ "key": "comando_join", "type": "string", "default": "JOIN",
"label": "Comando para unirse", "group": "basico" },
{ "key": "vidas_iniciales", "type": "number", "default": 3, "min": 1, "max": 10,
"label": "Vidas iniciales", "group": "basico" },
{ "key": "equipos", "type": "select", "default": "2",
"options": [ {"value": "2", "label": "2 equipos"}, {"value": "4", "label": "4 equipos"} ],
"label": "Cantidad de equipos", "group": "basico" },
{ "key": "gift_revive", "type": "gift-picker", "default": 5655,
"label": "Gift que revive al jugador", "group": "avanzado" },
{ "key": "modo_hardcore", "type": "boolean", "default": false,
"label": "Modo hardcore", "group": "avanzado" }
]
}
Campos raíz
| Campo | Req | Descripción |
|---|---|---|
manifestVersion | sí | Siempre 1. |
appId | sí | Id único, inmutable entre versiones. ^[a-z0-9][a-z0-9.-]{2,99}$ (3–100 chars). |
name | sí | Nombre corto interno. |
version | sí | Semver x.y.z. Cada publicación sube versión (estrictamente creciente). |
platform | sí | web | gta5 | gtasa | roblox | other. |
transport | sí | local-ws | local-poll | cloud-relay. Roblox obliga cloud-relay. |
events | sí | Subconjunto no vacío de: chat, gift, join, like, follow, share, live_status. El canal solo entrega lo declarado. |
listing | sí | Ficha pública (abajo). |
config | — | Schema de configuración (abajo). Puede ser []. |
listing — la ficha en Community
| Campo | Req | Reglas |
|---|---|---|
title | sí | ≤ 80 chars. |
shortDescription | sí | ≤ 160 chars (card del catálogo). |
description | sí | Markdown, ≤ 20k. Las instrucciones de instalación de un mod van aquí. |
coverImage | sí | Nombre del archivo que subes en el wizard. 16:9, ≤ 2 MB. |
screenshots | — | Hasta 6, mismas reglas que la carátula. |
videos | — | Links de YouTube (embed). No hospedamos video. |
downloadUrl | sí (mods externos) | Link EXTERNO de descarga. Con el flag hostedUploads puedes en cambio subir los archivos del mod. |
categoryIds | sí | 1–3 categorías existentes y visibles. Se validan al publicar. |
source — origen del código (solo platform: "web")
Los juegos web community se hospedan con nosotros y quedan en
https://communitygame.tikxander.com/<appId>/index.html. El manifest declara el origen:
"source": { "type": "upload" }
o, para importar desde un repositorio:
"source": { "type": "github", "repo": "https://github.com/tu/repo", "branch": "main", "path": "dist" }
- Con
type: "upload"subes un ZIP del build estático (requiere el flaghostedUploads). - Con
type: "github"clonamos el repo (branch+path). Si el repo es privado, el wizard te pide un token de SOLO lectura: se guarda cifrado, viaja fuera del manifest y puedes revocarlo tras la revisión. - Sin el flag
hostedUploads,platform: "web"exigetype: "github".
config — schema de configuración
Cada campo se renderiza en el portal del streamer. Reglas por campo:
| Regla | Detalle |
|---|---|
key | ^[a-z0-9_]+$, único en el array. |
type | string | number | boolean | select | color | gift-picker | media-picker. |
group | basico o avanzado (organiza la UI del streamer). |
default | OBLIGATORIO en TODOS los campos. |
select | requiere options: [{value, label}]; el default debe estar entre los value. |
number | opcional min/max (con slider), step, integer: true (fuerza enteros) y unit (sufijo mostrado, ej. "seg", "%"). El default debe caer en el rango. |
gift-picker | el streamer elige del catálogo real (imagen + diamantes), no tipea el id. Guarda el giftId de TikTok (estable entre regiones). Con "multiple": true guarda un number[] (varios regalos → mismo efecto); su default es un array. |
visibleIf | muestra el campo condicionalmente: { "field": "<otra_key>", "equals": <valor> } o { "field": "...", "oneOf": [<valores>] }. Ej.: mostrar mult_duracion_seg solo si mult_es_permanente === false. |
| Aviso | si dos gift-picker comparten el mismo giftId, el portal muestra una advertencia (el mismo regalo dispararía varios efectos). |
| Evolución | un key existente NO puede cambiar de type entre versiones (usa un key nuevo). |
Códigos de validación
El servidor devuelve un array [{field, code, message}] y el wizard los pinta EN VIVO sobre los
campos. Algunos códigos: manifest_version_invalid, app_id_invalid, app_id_immutable,
name_required, version_invalid, semver_not_increasing, platform_invalid,
transport_invalid, roblox_requires_cloud_relay, events_invalid, listing_title_invalid,
listing_short_invalid, listing_description_invalid, cover_required, too_many_screenshots,
video_url_invalid, categories_count_invalid, category_not_found, source_type_invalid,
source_github_required, bundle_required, github_source_incomplete, config_key_invalid,
config_key_duplicate, config_group_invalid, config_default_required,
config_options_required, config_default_not_in_options, config_default_type_mismatch,
config_default_out_of_range, config_type_changed.