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étodoRutaFinalidad
POST/playback/tokensAutorizar episodio → enlace WebView y manifest firmado
GET/playback/sessions/{session_id}Obtener sesión de reproducción
POST/playback/sessions/{session_id}/revokeFinalizar 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
  }'
CampoObligatorioNotas
title_idSí
episode_idSíDebe pertenecer al título
viewer_id_hashSíSHA-256(stable_viewer_id + salt) del socio. Production exige 64 hex minúsculas; Sandbox admite ID opacos heredados. Sin PII original.
countryProduction sí / Sandbox noISO 3166-1 alpha-2. Obligatorio en Production y validado contra territorios. Opcional en Sandbox; si está, se valida contra derechos visibles.
deviceNoPor ejemplo: web, ios, android
originCondicionalObligatorio si la clave tiene allowed_origins; debe coincidir con uno.
preferencesNoPreferencias sin PII para el WebView; véase abajo.
expires_inNoTTL 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.

CampoTipoNotas
preferred_languagesstring[] (≤20)Códigos BCP-47 o ISO en orden de preferencia
preferred_genresstring[] (≤50)Etiquetas de género del socio
subtitle_languagestringPista de subtítulos predeterminada
audio_languagestringPista de audio o doblaje predeterminada
autoplay_nextbooleanReproducir automáticamente el siguiente episodio
maturity_ratingstringClasificació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 aplica preferences; no requiere integrar reproductor. Contiene ID y ?t= firmado: trátelo como secreto y no lo almacene tras expires_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 /media o 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=1

La 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.

  1. La clave es válida y tiene playback:token; su entorno delimita catálogo y sesiones.
  2. El título es visible y el episodio le pertenece; si no, resource_not_found.
  3. El paquete está vigente y cumple status ∈ {CLEAR, RESTRICTED} y apiStreamingAllowed = true.
  4. El episodio está READY, con activo aprobado, no eliminado y HLS listo, cuyos derechos son CLEAR o RESTRICTED.
  5. Production: exige country; un acuerdo ACTIVE cubre título y territorio, tiene production_api_enabled = true e incluye el activo con accessLevel = stream. Sandbox: sin acuerdo; country, si está, se valida contra derechos visibles.
  6. Si hay allowed_origins, origin es obligatorio y debe coincidir. Con allowed_ips, debe coincidir la IP que emite el token. Solo aplica al emitir; el token no queda ligado a la IP del espectador.
  7. Límite: máximo PLAYBACK_MAX_CONCURRENT_SESSIONS sesiones activas (3 por defecto) por viewer_id_hash para acuerdo Production o título Sandbox. Exceder crea sesión BLOCKED y devuelve playback_concurrency_limit (429).

Motivos de error

error.codeHTTPCausa
resource_not_found404Título no visible, episodio ausente o de otro título
missing_scope403La clave carece de playback:token
license_not_active403Sin acuerdo ACTIVE, production_api_enabled false o sin paquete visible apto
territory_not_allowed403Falta country en Production, está fuera del acuerdo, falta origin o origin/IP bloqueado
episode_not_licensed403Episodio no READY, sin activo listo o fuera del conjunto licenciado
validation_failed422viewer_id_hash parece PII o preferences contiene clave desconocida o valor con PII ('@' o espacios)
playback_concurrency_limit429Demasiadas sesiones activas para espectador y acuerdo o título
conflict409Duplicado 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_hash debe 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 en preferences; 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_url ni manifests tras expires_at. webview_url contiene 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.