API de reproducción
Autorice a un espectador para un episodio y reciba un enlace WebView alojado más manifests HLS/DASH firmados para reproductor propio. Preferencias opcionales sin PII personalizan el WebView. Requiere playback:token.
Endpoints
| Método | Ruta | Finalidad |
|---|---|---|
| POST | /playback/tokens | Autorizar episodio → enlace WebView y manifest firmado |
| GET | /playback/sessions/{session_id} | Obtener sesión de reproducción |
| POST | /playback/sessions/{session_id}/revoke | Finalizar sesión activa |
Solicitud de token
curl -X POST "https://signal-partners.newunivers.ai/v1/playback/tokens" \
-H "Authorization: Bearer nsp_live_xxx" \
-H "X-NU-Partner-Id: org_acme" \
-H "X-NU-Request-Id: req_playback_0001" \
-H "Content-Type: application/json" \
-d '{
"title_id": "ttl_abc",
"episode_id": "ep_001",
"viewer_id_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"country": "KR",
"device": "web",
"origin": "https://app.acme.example",
"preferences": {
"preferred_languages": ["ko", "en"],
"preferred_genres": ["romance", "thriller"],
"subtitle_language": "ko",
"audio_language": "ko",
"autoplay_next": true,
"maturity_rating": "15"
},
"expires_in": 1800
}'| Campo | Obligatorio | Notas |
|---|---|---|
title_id | Sí | |
episode_id | Sí | Debe pertenecer al título |
viewer_id_hash | Sí | SHA-256(stable_viewer_id + salt) del socio. Production exige 64 hex minúsculas; Sandbox admite ID opacos heredados. Sin PII original. |
country | Production sí / Sandbox no | ISO 3166-1 alpha-2. Obligatorio en Production y validado contra territorios. Opcional en Sandbox; si está, se valida contra derechos visibles. |
device | No | Por ejemplo: web, ios, android |
origin | Condicional | Obligatorio si la clave tiene allowed_origins; debe coincidir con uno. |
preferences | No | Preferencias sin PII para el WebView; véase abajo. |
expires_in | No | TTL en segundos. Predeterminado 1.800; máximo 7.200; tope Production 1.800. |
preferences (señales del espectador)
Objeto opcional aplicado a subtítulos, audio, autoplay y recomendaciones, y guardado para analítica. El esquema es estricto; claves desconocidas devuelven 422. Sin PII; valores con @ o espacios devuelven 422 validation_failed.
| Campo | Tipo | Notas |
|---|---|---|
preferred_languages | string[] (≤20) | Códigos BCP-47 o ISO en orden de preferencia |
preferred_genres | string[] (≤50) | Etiquetas de género del socio |
subtitle_language | string | Pista de subtítulos predeterminada |
audio_language | string | Pista de audio o doblaje predeterminada |
autoplay_next | boolean | Reproducir automáticamente el siguiente episodio |
maturity_rating | string | Clasificación máxima del espectador |
El endpoint es idempotente mediante X-NU-Request-Id. Repetir el ID devuelve lo original; un duplicado aún en curso devuelve conflict (409).
Respuesta
{
"data": {
"playback_session_id": "pbs_123",
"expires_at": "2026-06-24T00:30:00Z",
"webview_url": "https://signal-partners.newunivers.ai/w/pbs_123?t=...",
"manifest": {
"hls": "https://signal-partners.newunivers.ai/media/hls/pbs_123/signal-city/ep001/master.m3u8?token=...",
"dash": "https://signal-partners.newunivers.ai/media/dash/pbs_123/signal-city/ep001/manifest.mpd?token=..."
},
"tracking": {
"event_endpoint": "https://signal-partners.newunivers.ai/v1/events",
"required_events": [
"EPISODE_STARTED",
"PLAYBACK_PROGRESS",
"EPISODE_COMPLETED"
]
}
}
}Hay dos formas equivalentes de reproducir la misma sesión autorizada. Elija una.
webview_url(recomendado): página de reproducción alojada por NU. Ábrala en WebView (WKWebView/android.webkit.WebView) o<iframe>. Resuelve el manifest y aplicapreferences; no requiere integrar reproductor. Contiene ID y?t=firmado: trátelo como secreto y no lo almacene trasexpires_at.manifest(reproductor propio): URL HLS/DASH firmadas. Cada solicitud valida firma, TTL, sesión y ruta aprobada. Nunca devuelve maestros. La validación se ejecuta en/mediao worker Cloudflare equivalente; el contrato URL es el mismo.
Ambos métodos se ligan al mismo playback_session_id; informe eventos de igual modo.
WebView de prueba
Antes de una sesión real, abra el reproductor de prueba para validar WebView o iframe. Reproduce de inmediato la vista 9:16 de Episode 009 Confession sin token, sesión ni configuración.
https://signal-partners.newunivers.ai/w/test?subtitle=ko&audio=ko&lang=ko&autoplay=1La prueba usa el mismo reproductor que webview_url. Si la muestra vertical funciona, los enlaces reales usan la misma política 9:16.
Orden de validación
Las comprobaciones son ordenadas. Si falta título o episodio devuelve resource_not_found (404). Tras resolverlos, un fallo devuelve 403 y registra block_reason en una sesión BLOCKED.
- La clave es válida y tiene
playback:token; su entorno delimita catálogo y sesiones. - El título es visible y el episodio le pertenece; si no,
resource_not_found. - El paquete está vigente y cumple
status ∈ {CLEAR, RESTRICTED}yapiStreamingAllowed = true. - El episodio está
READY, con activo aprobado, no eliminado y HLS listo, cuyos derechos sonCLEARoRESTRICTED. - Production: exige
country; un acuerdoACTIVEcubre título y territorio, tieneproduction_api_enabled = truee incluye el activo conaccessLevel = stream. Sandbox: sin acuerdo;country, si está, se valida contra derechos visibles. - Si hay
allowed_origins,origines obligatorio y debe coincidir. Conallowed_ips, debe coincidir la IP que emite el token. Solo aplica al emitir; el token no queda ligado a la IP del espectador. - Límite: máximo
PLAYBACK_MAX_CONCURRENT_SESSIONSsesiones activas (3 por defecto) porviewer_id_hashpara acuerdo Production o título Sandbox. Exceder crea sesiónBLOCKEDy devuelveplayback_concurrency_limit(429).
Motivos de error
| error.code | HTTP | Causa |
|---|---|---|
resource_not_found | 404 | Título no visible, episodio ausente o de otro título |
missing_scope | 403 | La clave carece de playback:token |
license_not_active | 403 | Sin acuerdo ACTIVE, production_api_enabled false o sin paquete visible apto |
territory_not_allowed | 403 | Falta country en Production, está fuera del acuerdo, falta origin o origin/IP bloqueado |
episode_not_licensed | 403 | Episodio no READY, sin activo listo o fuera del conjunto licenciado |
validation_failed | 422 | viewer_id_hash parece PII o preferences contiene clave desconocida o valor con PII ('@' o espacios) |
playback_concurrency_limit | 429 | Demasiadas sesiones activas para espectador y acuerdo o título |
conflict | 409 | Duplicado concurrente con X-NU-Request-Id aún en curso |
Sesiones
curl "https://signal-partners.newunivers.ai/v1/playback/sessions/pbs_123" \
-H "Authorization: Bearer nsp_live_xxx" -H "X-NU-Partner-Id: org_acme"{
"data": {
"playback_session_id": "pbs_123",
"title_id": "ttl_abc",
"episode_id": "ep_001",
"status": "issued",
"block_reason": null,
"preferences": { "subtitle_language": "ko", "autoplay_next": true },
"expires_at": "2026-06-24T00:30:00Z",
"started_at": null,
"completed_at": null
}
}La API devuelve el estado en minúscula. Flujo: issued → started → completed | expired | revoked | blocked. Emitir crea issued; EPISODE_STARTED avanza a started y llena started_at; EPISODE_COMPLETED avanza a completed y llena completed_at. Leer tras TTL proyecta expired sin persistir en este GET; un fallo crea blocked con block_reason. ID desconocido devuelve invalid_playback_session (400).
Revocar una sesión
curl -X POST "https://signal-partners.newunivers.ai/v1/playback/sessions/pbs_123/revoke" \
-H "Authorization: Bearer nsp_live_xxx" -H "X-NU-Partner-Id: org_acme"Marca sesiones issued/started como revoked y las quita del recuento. Devuelve estado final real; las terminadas siguen completed, expired o existente. /media comprueba al siguiente manifest, pero un token edge puede seguir válido por su TTL; manténgalo corto.
viewer_id_hash y caducidad
viewer_id_hashdebe ser SHA-256 de ID estable y sal de servidor. Production acepta 64 hex minúsculas; Sandbox conserva ID opacos. NU nunca acepta PII, tampoco enpreferences; rechaza valores con@o espacios.- Los tokens duran poco: 1.800 segundos por defecto, máximo 7.200, tope Production 1.800. Reemita al caducar; no almacene
webview_urlni manifests trasexpires_at.webview_urlcontiene token firmado: no lo registre ni comparta. - Revocar una clave o desactivar un acuerdo no cancela tokens emitidos. Mantenga TTL corto y reemita al caducar.
Informe actividad mediante API de eventos usando playback_session_id.