ExTV

개발자 — ExTV 오픈 API v1

방송 도구·후원 연출 서비스가 방송자의 동의를 받아 EX 후원을 실시간으로 받고, 시청자 후원 창에 보일 시그니처·BJ 목록을 올리는 API 예요.

1. 시작하기

  1. 연동 앱 등록을 요청해 주세요([email protected]) — 서비스 이름, 로그인 후 돌아올 주소(redirect_uri, https)를 알려 주시면 client_id·client_secret을 테스트용·운영용 두 쌍으로 따로 전달해요.
  2. 테스트용과 운영용은 같은 주소(https://extv.co.kr/api/openapi/v1)를 쓰고 키만 달라요. 테스트 키로 연결하면 동의 화면에 ‘테스트’ 표시가 붙어요.
  3. client_secret 은 서버에만 두고 로그·화면·저장소에 남기지 마세요. 새어 나갔으면 바로 알려 주세요(다시 발급).
권한(scope)내용
donation.read내 채널로 들어온 EX 후원 받기(후원자 닉네임·금액·메시지)
catalog.write후원 창에 보일 시그니처·BJ 목록 올리기

2. 방송자 동의(OAuth 2.0 Authorization Code)

방송자를 아래 주소로 보내면 ExTV 로그인 → 동의 화면을 거쳐 redirect_uri?code=…&state=… 로 돌아와요(거부하면 error=access_denied). 승인된 채널이 있는 회원만 연결할 수 있고, 채널 하나에 앱마다 연결 하나예요(다시 동의하면 예전 연결은 끝나요).

GET https://extv.co.kr/api/openapi/v1/oauth/authorize?response_type=code
    &client_id={client_id}&redirect_uri={등록한 주소}&state={임의 값}
    &scope=donation.read%20catalog.write

받은 code(10분, 1회용)를 서버에서 토큰으로 바꿔요. 본문은 JSON 또는 form-urlencoded 둘 다 받아요.

POST https://extv.co.kr/api/openapi/v1/oauth/token
{ "grant_type": "authorization_code", "code": "exc_…", "client_id": "…", "client_secret": "…", "redirect_uri": "…" }

→ 200 { "access_token": "exa_…", "token_type": "Bearer", "expires_in": 3600,
        "refresh_token": "exr_…", "scope": "donation.read catalog.write" }
  • access_token 은 60분, refresh_token 은 60일 — {"grant_type":"refresh_token","refresh_token":"exr_…",client_id,client_secret} 로 새로 받아요.
  • refresh 는 쓸 때마다 새로 발급되고 이전 것은 바로 무효예요. 받은 쌍으로 반드시 교체 저장하세요(이전 access_token 도 무효).
  • 오류: 400 {"error":"invalid_grant"} = 연동 만료·해제(다시 동의 필요), 401 invalid_client = 키 틀림, invalid_request = 형식 오류.
  • 해제: POST /oauth/revoke {token, client_id, client_secret} — 항상 200. 방송자도 ‘방송하기 → 외부 연동’에서 끊을 수 있어요.

3. API (Authorization: Bearer {access_token})

요청권한내용
GET /me-채널 정보 {apiVersion, channelId, handle, name, profileImage, isLive, broadcastId, exWon, scope}
PUT /catalogcatalog.write시그니처·BJ 목록 전체 교체(아래 4장)
GET /socket-infodonation.read실시간 소켓 주소 + 1회용 ticket(60초) {onAir, broadcastId, socketUrl, ticket, exWon}
GET /donations?after={cursor}&limit=donation.read놓친 후원 다시 받기 — {items:[…], hasMore}, limit 최대 100. 연결(동의)한 뒤의 후원만
POST /donations/{id}/ackdonation.read받아서 처리했다고 알림(선택, 여러 번 와도 됨)

오류 응답은 {error, message} — 401 invalid_token(만료면 refresh 로 새로), 403 insufficient_scope, 429 요청이 너무 잦음.

4. 시그니처·BJ 목록 (PUT /catalog)

시청자가 ExTV 후원 창에서 고를 목록이에요. 받은 그대로 교체하니 항상 전체를 보내 주세요(바뀔 때마다, 몇 초 모아서).

{
  "version": "2026-10-30T12:00:00Z",
  "signatures": [
    { "id": "sig_1", "title": "두산아웃송", "amount": 20100, "imageUrl": "https://…" | null, "sort": 1 },
    { "id": "random_3000", "title": "랜덤시그", "amount": 3000, "isRandom": true }
  ],
  "bjs": [ { "id": "b12", "name": "BJ 하나", "imageUrl": null, "sort": 1 } ],
  "excelOn": true
}
  • amount 는 원 단위 — 1 EX = 1원이라 같은 숫자가 EX 로 보여요. 이미지가 없으면 기본 그림으로 보여요.
  • isRandom 항목을 고른 후원은 signatureId 없이 금액만 와요(랜덤 규칙은 연동 서비스 쪽).
  • excelOn: false 면 BJ 지정 칸이 숨겨져요.

5. 실시간 소켓

socketUrl 에 ?ticket=… 을 붙여 WebSocket 으로 연결해요(JSON 텍스트 1건씩). 같은 채널에 여러 개 붙어도 모두 같은 이벤트를 받아요 — 같은 후원을 두 번 처리하지 않게 id 로 거르세요.

→ { "type": "hello", "apiVersion": 1, "channelId": 31, "onAir": true, "broadcastId": 812, "exWon": 1 }
→ { "type": "ping" }                       // 약 5초마다 — {"type":"pong"} 또는 아무 메시지로 답
→ { "type": "donation",
    "id": "exd_123", "cursor": 123, "at": "2026-10-30T21:03:11.000Z",
    "channelId": 31, "broadcastId": 812,
    "donor": { "id": "공개 id" | null, "nickname": "홍길동" },
    "ex": 22200, "won": 22200, "exWon": 1,
    "message": "화이팅", "signatureId": "sig_1" | null, "bjId": "b12" | null, "anonymous": false }
  • 금액 = ex × exWon 으로 계산하고 won 과 맞춰 보세요. exWon(1 EX 가 몇 원인지)이 바뀌면 ExTV 가 연결된 소켓에 새 hello 를 먼저 보내요.
  • 30초 동안 아무 메시지가 없으면 서버가 끊어요. 끊기면 socket-info 부터 다시(티켓은 1회용) — 붙은 뒤 GET /donations?after=마지막 cursor 로 놓친 후원을 먼저 받으세요.
  • 닫힘 코드 4001 = 티켓 틀림·만료(새로 받기), 4013 = 연결 해제됨(다시 동의 전까지 재연결 금지). 모르는 type 은 무시하세요.
  • 후원 취소 이벤트는 없어요(EX 는 쓰기 전에만 환불돼요).

6. 지켜 주세요

  • 후원자 닉네임·메시지는 방송 연출에만 쓰고 다른 목적으로 모으거나 넘기지 마세요.
  • 요청은 1분에 1,200번(토큰 120번)까지 — 넘으면 429. /me 는 1분에 한 번이면 충분해요.
  • 응답에 새 필드가 늘 수 있어요. 모르는 필드는 무시하세요(v1 안에서 기존 필드의 뜻은 바뀌지 않아요).

문의: [email protected]