재생 API
한 시청자가 한 에피소드를 보도록 인가하고 즉시 WebView에서 재생할 호스팅 웹뷰 링크와 자체 플레이어용 동등한 서명 HLS/DASH 매니페스트를 받습니다. 선택적 비PII 환경설정으로 웹뷰를 개인화할 수 있습니다. playback:token가 필요합니다.
엔드포인트
| 메서드 | 경로 | 용도 |
|---|---|---|
| POST | /playback/tokens | 인가된 에피소드 → 웹뷰 링크 + 서명 매니페스트 |
| GET | /playback/sessions/{session_id} | 재생 세션 조회 |
| POST | /playback/sessions/{session_id}/revoke | 활성 세션 종료 |
토큰 요청
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
}'| 필드 | 필수 | 비고 |
|---|---|---|
title_id | 예 | |
episode_id | 예 | 해당 타이틀에 속해야 함 |
viewer_id_hash | 예 | 파트너 측 SHA-256(stable_viewer_id + salt). Production은 정확히 64자 소문자 16진수를 요구하고 Sandbox는 기존 opaque ID를 허용합니다. 원시 PII 금지. |
country | Production 예 / Sandbox 아니오 | ISO 3166-1 alpha-2. Production에서 필수이며 계약 지역과 대조합니다. Sandbox에서는 선택이며 전달하면 노출된 권리 지역과 대조합니다. |
device | 아니오 | 예: web, ios, android |
origin | 조건부 | 키에 allowed_origins가 있으면 필수이며 그중 하나와 일치해야 합니다. |
preferences | 아니오 | 호스팅 웹뷰용 비PII 시청자 취향 신호. 아래 참고. |
expires_in | 아니오 | 토큰 TTL(초). 기본 1,800, 절대 최대 7,200, Production 기본 상한 1,800. |
preferences(시청자 취향 신호)
호스팅 웹뷰가 자막·오디오 기본값, 자동 재생, 추천에 적용하고 분석용으로 세션에 저장하는 선택 객체입니다. 스키마는 엄격하여 알 수 없는 키는 422를 반환합니다. 값에 원시 PII가 없어야 하며 @ 또는 공백이 있으면 422 validation_failed를 반환합니다.
| 필드 | 타입 | 비고 |
|---|---|---|
preferred_languages | string[] (≤20) | 선호 순서의 BCP-47 또는 ISO 언어 코드 |
preferred_genres | string[] (≤50) | 파트너 측 장르 태그 |
subtitle_language | string | 기본 자막 트랙 |
audio_language | string | 기본 오디오 또는 더빙 트랙 |
autoplay_next | boolean | 다음 에피소드 자동 재생 |
maturity_rating | string | 시청자의 최대 관람 등급 |
이 엔드포인트는 선택적 X-NU-Request-Id로 멱등하게 동작합니다. 같은 ID 재시도는 원본 응답을 반환하고 처리 중인 동시 중복은 conflict(409)를 반환합니다.
응답
{
"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"
]
}
}
}동일한 인가 세션을 재생하는 두 가지 동등한 방법이 제공됩니다. 하나를 선택하세요.
webview_url(권장): NU가 호스팅하는 재생 전용 페이지입니다. WebView(WKWebView/android.webkit.WebView) 또는<iframe>에서 여세요. 내부적으로 매니페스트를 해석하고 저장된preferences를 적용하므로 플레이어 연동이 필요 없습니다. 링크에는 세션 ID와 서명된?t=이 있으므로 시크릿으로 취급하고expires_at이후 캐시하지 마세요.manifest(자체 플레이어): 자체 플레이어 운영 파트너를 위한 서명 HLS/DASH URL입니다. 각 매니페스트·세그먼트 요청에서 서명, TTL, 세션 ID 및 승인 미디어 경로를 검증합니다. 원본 마스터 파일은 반환하지 않습니다. 검증은 앱의/media또는 동등한 Cloudflare 엣지 워커에서 실행되며 URL 규약은 같습니다.
두 방식 모두 같은 playback_session_id에 바인딩되며 어느 쪽이든 동일하게 이벤트를 보고합니다.
테스트 웹뷰
실제 세션 전 테스트 플레이어를 열어 WebView 또는 iframe 연동을 검증하세요. 토큰, 세션 또는 미디어 설정 없이 Episode 009 Confession 9:16 프리뷰를 즉시 재생합니다.
https://signal-partners.newunivers.ai/w/test?subtitle=ko&audio=ko&lang=ko&autoplay=1테스트는 webview_url과 동일한 플레이어를 사용합니다. 세로형 샘플이 WebView에서 재생되면 실제 링크도 같은 9:16 화면 정책을 사용합니다.
검증 순서
검사는 순서대로 실행됩니다. 타이틀 또는 에피소드가 없으면 resource_not_found(404)를 반환합니다. 리소스 확인 후 인가 실패는 403을 반환하고 BLOCKED 세션에 block_reason을 기록합니다.
- API 키가 유효하고
playback:token를 보유합니다. 키 환경이 카탈로그 및 세션 접근 범위를 결정합니다. - 타이틀이 해당 환경에 노출되고 에피소드가 타이틀에 속해야 하며 그렇지 않으면
resource_not_found입니다. - 권리 패키지가 만료되지 않았고
status ∈ {CLEAR, RESTRICTED}및apiStreamingAllowed = true을 충족해야 합니다. - 에피소드가
READY이고 승인·미삭제·HLS 준비 완료 영상 에셋이 있으며 에셋 권리는CLEAR또는RESTRICTED여야 합니다. - Production:
country가 필수이고ACTIVE계약이 타이틀·지역을 포괄하며production_api_enabled = true이고 준비된 영상 에셋을accessLevel = stream으로 포함해야 합니다. Sandbox: 계약은 필요 없으며country가 있으면 노출 권리 지역과 대조합니다. - 키에
allowed_origins가 있으면origin이 필수이고 일치해야 합니다.allowed_ips가 설정되면 토큰 발급 요청 IP가 일치해야 합니다. 발급 시점에만 적용되며 토큰은 시청자 IP에 바인딩되지 않습니다. - 동시 세션 상한: Production 계약 또는 Sandbox 타이틀별
viewer_id_hash당 최대PLAYBACK_MAX_CONCURRENT_SESSIONS개(기본 3) 활성 세션입니다. 초과 발급은BLOCKED세션을 만들고playback_concurrency_limit(429)를 반환합니다.
오류 사유
| error.code | HTTP | 원인 |
|---|---|---|
resource_not_found | 404 | 타이틀이 환경에 비공개이거나 에피소드가 없거나 다른 타이틀에 속함 |
missing_scope | 403 | 키에 playback:token이 없음 |
license_not_active | 403 | ACTIVE 계약이 없거나 production_api_enabled가 false이거나 노출된 스트리밍 가능 권리 패키지가 없음 |
territory_not_allowed | 403 | Production country 누락, country가 계약·권리 지역 밖, 필수 origin 누락 또는 origin/IP 차단 |
episode_not_licensed | 403 | 에피소드가 READY가 아니거나 준비된 영상 에셋이 없거나 에셋이 라이선스 집합 밖 |
validation_failed | 422 | viewer_id_hash가 원시 PII로 보이거나 preferences에 알 수 없는 키 또는 원시 PII('@'나 공백)가 있음 |
playback_concurrency_limit | 429 | 이 시청자와 계약 또는 타이틀의 활성 세션이 너무 많음 |
conflict | 409 | 처리 중 X-NU-Request-Id를 사용한 동시 중복 요청 |
세션
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
}
}API는 저장된 세션 상태를 소문자로 반환합니다. 흐름: issued → started → completed | expired | revoked | blocked. 토큰 발급은 issued를 만들고 EPISODE_STARTED가 started로 전진시키며 started_at을 채웁니다. EPISODE_COMPLETED는 completed로 전진시키고 completed_at을 채웁니다. TTL 이후 조회는 expired를 저장하고 인가 실패는 block_reason이 있는 blocked를 만듭니다. 알 수 없는 ID는 invalid_playback_session(400)를 반환합니다.
세션 폐기
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"활성 issued/started 세션을 revoked로 표시하여 동시 세션 수에서 제외합니다. 응답은 실제 최종 상태를 반환하며 종료된 세션은 completed, expired 또는 기존 상태를 유지합니다. 앱 /media 경로는 다음 매니페스트 요청에서 폐기를 확인하지만 기존 엣지 워커 토큰은 TTL 동안 유효할 수 있으므로 TTL을 짧게 유지하세요.
viewer_id_hash 및 만료
viewer_id_hash는 안정적인 시청자 ID와 서버 보관 솔트의 SHA-256이어야 합니다. Production은 정확히 64자 소문자 16진수를 허용하고 Sandbox는 기존 opaque ID를 유지합니다. NU는preferences를 포함해 원시 PII를 수락하거나 저장하지 않으며@또는 공백 값은 거부합니다.- 토큰은 수명이 짧습니다: 기본 1,800초, 절대 최대 7,200초, Production 기본 상한 1,800초. 만료 시 재발급하고
expires_at이후webview_url또는 매니페스트 URL을 캐시하지 마세요.webview_url에는 서명 토큰이 있으므로 로그나 공유 링크에 넣지 마세요. - 키를 폐기하거나 계약을 비활성화해도 이미 발급된 재생 토큰은 취소되지 않습니다. TTL을 짧게 유지하고 만료 시 재발급하세요.
반환된 playback_session_id를 사용하여 이벤트 API로 재생 활동을 보고하세요.