Integrá Zyplo
- Subís un archivo HTML. Lo ponemos en el feed.
- No te cobramos, no te pagamos, no te atamos.
- Tu perfil de creador linkea a tu itch, tu Steam, tu Patreon.
Para quién no es
Si exportás WebGL multi-archivo de Godot, Unity o GameMaker, Zyplo no lo va a hostear. El formato es un solo archivo HTML autocontenido — un .wasm más un .pck no se pueden subir, ni hoy ni en un roadmap. Mejor que lo sepas ahora y no después de una semana de intentarlo.
Publicar un juego
Zyplo habla MCP. Lo conectás una vez y tu asistente puede crear un juego, validarlo, subirlo como borrador, editarlo y publicarlo — sin que salgas del chat.
- Claude — En Claude, agregá un conector personalizado con esta URL. El login pasa en el navegador; no hay key que copiar ni guardar.
https://zyplo.io/api/mcp - ChatGPT — La misma idea, otro endpoint. Activá el Modo Desarrollador en ChatGPT y agregá el conector.
https://zyplo.io/api/chatgpt/mcp - Cursor, Codex y otros clientes MCP — Pegá esto en la config MCP de tu cliente. Cualquier cliente que hable streamable HTTP y OAuth funciona.
La auth es OAuth con registro dinámico de cliente y PKCE. Las keys zyplo_sk_ viejas siguen funcionando si ya tenés una.
Por HTTP
Un POST publica un juego. No hace falta nada más — sin SDK, sin build step, sin script tag adentro de tu juego.
curl -X POST https://zyplo.io/api/upload-game \
-H "Authorization: Bearer $ZYPLO_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Tap the Comet","description":"One tap. Do not miss.","html_content":"<!doctype html>...","tags":["arcade"],"published":false}'
ZYPLO_KEY es una key personal (zyplo_sk_…) o un access token de OAuth (zyplo_at_…). Subí primero como borrador (published: false) y miralo antes de que salga en vivo. openapi.json
Chequeá antes de subir
Agregá ?dry_run=true y la misma llamada valida sin crear nada. Reporta todos los problemas de una en vez de frenar en el primero, y no escribe nada — ni archivo, ni juego, ni marca de rate limit — así que podés iterar todo lo que quieras.
curl -X POST "https://zyplo.io/api/upload-game?dry_run=true" \
-H "Authorization: Bearer $ZYPLO_KEY" \
-H "Content-Type: application/json" \
-d @game.json
Responde 200 esté el juego bien o mal: el status dice que el chequeo corrió, ok dice el veredicto. Un token inválido sigue respondiendo 401. No chequea ni el rate limit ni la portada, así que ok: true quiere decir que el juego es válido, no que la subida vaya a funcionar.
El formato
Estas son las reglas reales, leídas del mismo módulo que usan el uploader y el prompt del MCP. Si esta página y la API alguna vez no coinciden, eso es un bug — avisanos.
Reglas duras — la subida rechaza si las violás
- Un solo archivo autocontenido. Sin scripts externos, sin CSS externo, sin
<link rel="stylesheet">, sinfetch/XMLHttpRequest/WebSocket/EventSource, sineval/new Function, sin cookies, sinwindow.open, sin<iframe>, sin<form>, sin<meta http-equiv="refresh">. - Las URLs de assets tienen que ser
data:,blob:ohttps://absoluta. Nada de rutas relativas. - El HTML no puede pasar de 5 MB. Apuntá a menos de 90.000 caracteres para que el juego también funcione por ChatGPT.
- El documento arranca con
<!doctype html>o<html.
Reglas de diseño — no se validan, pero si las ignorás el feed te entierra
- Viewport:
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">. - Layout vertical 9:16 fijo. Ocupa toda la pantalla. Sin scroll.
- Controles solo de tap/swipe — sin teclado, sin hover de mouse. Usá
pointerdown/touchstart. - Duración del loop: un intento completo dura 10–30 segundos. El juego reinicia con un tap.
- Mostrá una pantalla de título de una línea, un tap-to-start obvio, un score visible, y una pantalla de game over con botón de reinicio.
- Visuales vibrantes y de alto contraste. Colores fuertes, CSS y SVG/canvas. Sin imágenes externas, sin fuentes.
- Performance: tiene que correr fluido en un teléfono de 4 años. Evitá física pesada o canvas gigantes.
5 MB— tamaño máximo del HTML90k— caracteres — quedate abajo de esto para que también funcione por ChatGPT10–30s— un intento completo10s— entre subidas
Un juego que pasa
Un juego mínimo que cumple todas las reglas de arriba. Jugalo y después leelo — es corto a propósito. Jugar el ejemplo
Es un solo archivo, así que el Ver código fuente de tu navegador te muestra el juego entero.
Por qué se rechaza una subida
Todo error responde con un code estable, el campo que falla, y qué hacer al respecto. Ramificá sobre code, nunca sobre el texto del mensaje — los mensajes se pueden reescribir, los códigos no.
{
"error": "Zyplo acepta HTML autocontenido: ...",
"code": "external_script",
"field": "html_content",
"detail": "https://cdn.example/phaser.js",
"fix": "Inline the library into a <script> block.",
"docs_url": "https://zyplo.io/en/integrate#error-external_script"
}
El string error no cambió respecto de antes de que existieran los códigos, así que nada de lo que lo lea se rompe. code es internal_error cuando el error es uno que todavía no describimos — eso es un bug nuestro y lo queremos saber.
not_html400- Lo que mandaste no arranca como un documento HTML. Campo:
html_contentSolución: Mandá el documento entero, empezando con<!doctype html>. No un fragmento, no JSON, no un blob en base64. external_script400- Un
<script src="...">apunta afuera. Campo:html_contentSolución: Inliná la librería dentro de un bloque<script>. Si es muy grande para inlinear, el juego no entra en Zyplo. relative_asset400- Un asset usa ruta relativa, y no tiene contra qué resolver una vez que el archivo se sirve solo. Campo:
html_contentSolución: Embebé el asset comodata:URI, o apuntá a una URLhttps://absoluta. forbidden_tag400- El documento tiene
<iframe>,<frame>,<frameset>,<object>,<embed>,<applet>,<form>o<base>. Campo:html_contentSolución: Sacalo. Los juegos ya corren dentro de un iframe con sandbox, así que anidar otro o postear un form no puede funcionar. meta_refresh400- Un
<meta http-equiv="refresh">sacaría al jugador del feed. Campo:html_contentSolución: Sacalo y reiniciá el juego desde JavaScript. dangerous_url400- Una URL usa un esquema que no está permitido (
javascript:,vbscript:,file:,ftp:, ohttp:pelado). Campo:html_contentSolución: Usáhttps://,data:oblob:. Para comportamiento al hacer click, poné un listener en vez de una URLjavascript:. network_api400- El código llama a
fetch,XMLHttpRequest,WebSocket,EventSource,importScripts,eval,new Function,navigator.sendBeacon,document.cookieowindow.open. Campo:html_contentSolución: Sacá la llamada. Los juegos son offline por diseño: guardá el estado en memoria o enlocalStorage. No hay backend in-game ni SDK in-game. missing_token401- No se mandó el header Authorization. Solución: Mandá
Authorization: Bearer <token>con una key zyplo_sk_ o un token OAuth zyplo_at_. invalid_token_format401- El token Bearer no es ni una key zyplo_sk_ ni un token OAuth zyplo_at_. Solución: Fijate que no estés mandando un JWT de Supabase o un token de sesión. Los tokens de Zyplo empiezan con zyplo_.
invalid_api_key401- La key zyplo_sk_ no existe o fue revocada. Solución: Generá una key nueva en Perfil de creador → API & Connectors, o conectate por OAuth.
invalid_oauth_token401- El access token de OAuth expiró o fue revocado. Solución: Refrescá el token. Si tu cliente no puede, sacá y volvé a agregar el conector de Zyplo.
account_suspended401- La cuenta detrás de este token está suspendida, así que no puede subir ni editar juegos. Solución: No hay nada que reintentar. Escribinos si creés que la suspensión es un error.
game_not_found401- No hay ningún juego con ese game_id en esta cuenta. Puede haber sido borrado, o el id puede ser de otro. Solución: Llamá a list_my_games (MCP) para ver los ids que esta cuenta realmente tiene. No reintentes con el mismo id.
invalid_body400- La request llegó sin un objeto JSON como body. Campo:
bodySolución: Mandá los campos como un objeto JSON con Content-Type: application/json. Una request vacía, un string suelto y un array caen todos acá;detaildice cuál llegó. missing_field400- Faltaba un campo requerido, o vino vacío. Solución: Mirá
fieldpara saber cuál. Mandalo con un valor no vacío. conflicting_fields400- Se mandaron dos campos que no pueden aplicar a la vez. Solución: Mandá cover_url o cover_data_url, nunca los dos.
unsupported_tag400- Un tag no es de los veinte que Zyplo acepta. Campo:
tagsSolución: Elegí del enum del spec OpenAPI (arcade, puzzle, skill, casual, humor, meme, snake, breakout, tetris, dodge, memory, logic, reflex, timing, precision, runner, retro, action, platformer, strategy). Los tags después del décimo se descartan en silencio. no_updates400- La llamada de update no traía ningún campo con cambios, así que no hay nada que escribir. Solución: Incluí al menos uno de title, description, tags, cover_url o cover_data_url.
unknown_action400- El campo
actionno es uno que este endpoint maneje. Campo:actionSolución: Usá update, set_visibility o delete. invalid_dry_run400- El parámetro de query dry_run traía un valor que no es ni sí ni no. Campo:
dry_runSolución: Mandá ?dry_run=true (o 1) para validar, ?dry_run=false (o 0) para subir, o no lo mandes. Un valor no reconocido se rechaza en vez de adivinarse: adivinar mal subiría el juego de verdad. method_not_allowed405- Este endpoint sólo responde POST. Solución: Mandá la request como POST con un body JSON.
html_too_large400- El documento HTML pasa el límite de tamaño. Campo:
html_contentSolución: Achicá el documento. Lo que suele pasarse son los assets en base64 embebidos — generá el arte con canvas o SVG. cover_too_large400- La imagen de portada pasa el límite de tamaño. Campo:
cover_urlSolución: Mandá una imagen más chica. plan_limit_reached400- La cuenta ya tiene el máximo de juegos que permite su plan. Solución: Borrá un juego antes de subir otro. Reintentar no va a servir.
rate_limited400- Las subidas de esta cuenta vienen demasiado seguidas. Solución: Esperá los segundos que dice el mensaje y reintentá la misma request una vez.
game_under_review400- El juego está retenido para revisión de moderación, así que no se puede editar ni publicar. Solución: Esperá a que termine la revisión. Reintentar ahora va a fallar igual.
game_file_missing400- El archivo HTML guardado de este juego no está, así que no se puede leer su código. Solución: Volvé a subir el juego. Esta es nuestra — avisanos que pasó.
cover_invalid_type400- La portada no es JPG, PNG ni WebP. Campo:
cover_urlSolución: Convertila a alguno de esos tres. cover_invalid_data400- Los bytes de la portada no decodifican como el tipo de imagen que dicen ser. Campo:
cover_urlSolución: Re-exportá la imagen. Una extensión renombrada o una descarga cortada caen las dos acá. cover_fetch_failed400- No se pudo descargar la cover_url. Campo:
cover_urlSolución: Mandá una URL https pública que devuelva la imagen directo. Zyplo rechaza http, direcciones privadas y de loopback, y sigue como máximo 3 redirects, que también tienen que ser https públicos. invalid_report400- El payload del reporte está mal formado. Solución: Mandá un game_id válido (UUID) y una razón conocida.
duplicate_report409- Este reportante ya reportó este juego. Solución: No hay nada que hacer — vale el primer reporte.
report_rate_limited429- Demasiados reportes desde este origen. Solución: Esperá antes de reportar de nuevo.
storage_upload_failed500- Zyplo no pudo escribir el archivo en storage. Solución: Reintentá una vez. Si vuelve a pasar es una falla nuestra — avisanos.
server_misconfigured500- Falta una credencial del lado del servidor. Solución: No hay nada que puedas hacer. Avisanos.
internal_error500- Algo falló de nuestro lado que no anticipamos. Solución: Reintentá una vez y después avisanos.
¿Trabado?
Escribime un mail con lo que mandaste y lo que te volvió. Contesta una persona — normalmente el que escribió esta página.